web-dev-qa-db-ja.com

「安全なトークン」を事前に生成

私たちのウェブアプリでは、使い捨てトークンをうまく利用しています。たとえば、誰かが3つの「アカウント」のいずれかを作成したり、パスワードをリセットしたり、ランダムな使い捨てトークンを必要とするその他の数のことをしたりした場合、次のようになります。

  1. OSのCSPRNG からランダムな32バイトを生成します。
    1. Base-64 URLエンコード
    2. ハッシュをデータベースに保存します(N = 2 ^ 15、r = 8、P = 1のScrypt)
    3. ユーザーに使い捨てトークンを提供します(通常はURLの形式)

これはすべて正常に動作します。ただし、プロファイリングを通じて、API呼び出しの一部はトークンの確認と作成の両方を行う必要があるため、数秒かかることがわかりました。一部のAPI呼び出しは、Scryptを3回以上使用します。

ひどくはありませんが、可能であればAPI呼び出しを高速化したいと考えています。

私はいくつかのランダムなトークンを事前に生成するという考えを浮かび上がらせましたが、先に進んでこれを始める前に、セカンドオピニオンをお願いします。

これについては、2つの方法があると思います。まず、単純に(トークン、ハッシュ、ソルト)トリプルを作成し、メモリに格納します。これの欠点は、暗号化されていないトークンがそこにあるだけであり、攻撃者がそれらを取得して使用できることです。ただし、そのためには特定のAWSインスタンスへのアクセスが必要であり、1回限りのトークンはその状況での懸念が最も少ないでしょう。

2つ目は、ある種のボールトを使用することです。そのため、タプルはまだメモリ内にありますが、暗号化されています。これは安全に聞こえますが、ボールトのパスワードをどこかに置く必要があるため、攻撃者がAWSインスタンスにアクセスできる場合は、ボールトへのアクセスが可能です。

考え?私はこの考えに少し警戒していますが、いくつかのAPI呼び出しを3秒から1秒未満に減らしたいと思っています。

2
Eric Lagergren

概要:あなたのような本当にランダムなデータに対してscryptを使用する必要はありません。 SHA256のような安全なハッシュを使用するだけで大​​丈夫です。

説明:SHA256のようなハッシュは、私たちの知る限りでは不可逆的です。ハッシュから元のテキストに移動する唯一の方法は、事前計算と総当たりの組み合わせを使用することです。人々は弱い予測可能なパスワードを選択するため、事前計算はパスワードの保存に非常に成功していることがわかります。これが、パスワードハッシュの科学全体を作成する必要がある理由です。

ただし、256ビットのエントロピーがあります。 Rainbow table (この惑星に格納するスペースがない)または総当たり(太陽は途中で爆発する)を生成することは不可能です。大きな入力スペース。

ですから、ランダムデータに高速のSHA256を使用して、それが解読不可能であることを確認してください。

1
Neil Smithline

トークンを事前に生成すると、攻撃者がトークンにアクセスすることを心配していると言います。これは技術的にはより大きなウィンドウを提供しますが、攻撃者がとにかく生成されている安全なトークンを監視することははるかに困難です。

私たちがパスワードをハッシュ化する理由は、今後数年間の違反がパスワードを明らかにしないためです。この場合、過去の違反からわずか1分が問題です。 1分前に誰かがあなたに違反した場合、彼らもおそらくあなたが知っている違反をすることができます。

私の提案はこれです:

  • たとえば、600の安全なトークンを生成するプログラムを用意します。このプログラムのメモリを他のプログラムやプロセスからできるだけ分離します。 (たとえば、他のユーザーとは別のユーザーにします。)
  • どのプロセスもプログラムからトークンを要求できます。トークンが要求されると、プールから削除されます。
  • あなたがどれだけ偏執的であるかに応じて、プログラムに毎秒10個の最も古いトークンを破棄させ、10個の新しいトークンを生成させます。この方法では、トークンは1分間しか持続できません。

ニールの答え も正しいです(何らかの理由でSha256の動作が遅い場合は、上記を使用できます)。

0
PyRulez

ランダムトークンでscryptを実行するのではなく、このような状況では、hmacを生成します。 redditのオープンソースの電子メール購読解除トークンコード の例を次に示します。

def generate_notification_email_unsubscribe_token(user_id36, user_email=None,
                                                  user_password_hash=None):
    """Generate a token used for one-click unsubscribe links for notification
    emails.
    user_id36: A base36-encoded user id.
    user_email: The user's email.  Looked up if not provided.
    user_password_hash: The hash of the user's password.  Looked up if not
                        provided.
    """
    import hashlib
    import hmac

    if (not user_email) or (not user_password_hash):
        user = Account._byID36(user_id36, data=True)
        if not user_email:
            user_email = user.email
        if not user_password_hash:
            user_password_hash = user.password

    return hmac.new(
        g.secrets['email_notifications'],
        user_id36 + user_email + user_password_hash,

それが1回だけ使用されることを確認する必要がある場合は、現在行っているように、その値をデータベースに追加で挿入し、一度使用したら削除できます。