web-dev-qa-db-ja.com

パスワードをハッシュする前にソルトにユーザー名を追加するのはなぜですか?

H(ユーザー名+ソルト+パスワード)のパスワードハッシュの例を見てきました。ユーザー名を追加する目的は何ですか?何か目的はありますか?

78
PaintingInAir

いいえ、目的はありません。これはセキュリティシアター*です。ソルトの目的は、ハッシュされたすべてのパスワードに対して並列攻撃を実行不可能にし、レインボーテーブルを破壊することです。そこにユーザー名を追加しても、その動作は改善されず、セキュリティのその他の側面も向上しません。ユーザー名を変更すると問題が発生し、より複雑で非標準のシステムを維持する必要があるため、実際には少し欠点があります。純粋に暗号化の意味では、マイナス面はありません。しかし実際には、複雑さが増すほどバグが増えることになります。

Saltとユーザー名のプロパティ

ユーザー名自体は公開されていない可能性があるため、それを追加のシークレットとして使用しても害はないと思われるかもしれませんが、データベースにはユーザー名がプレーンテキストで含まれている可能性が高く、この疑わしい利点は無効になります。人々は既存の認証技術に固執するべきです。しかし、これらの各オブジェクトのプロパティを見てみましょう。

塩は:

  • 非公開-ソルトはプレーンテキストで保存されます。

  • 安全-それらはランダムに生成され、長いです。

  • ユニーク-すべてのユーザーのソルトは意図的に異なります。

パスワードは:

  • 秘密-付箋に書かれていないと仮定します。

  • 安全-それがそうでない場合 hunter2 。パスワードは good である必要があります。

  • ユニーク-理想的には、少なくともすべてのパスワードが理想的ではありません。

これをユーザー名と比較してください。ユーザー名は次のとおりです。

  • 秘密ではない-それらは公開されているか、少なくとも プレーンテキストで保存 です。

  • 安全ではありません-長くて複雑なユーザー名を選択しようとする人はいません。

  • 一意ではない-ユーザー名はサイト間で共有しても安全です。

良い塩の特徴

さて、 正確には塩は何をするのですか ?一般に、良い塩には3つの利点があります。

  1. 攻撃者がすべてのユーザーのハッシュを一度に攻撃するのを防ぎます。攻撃者は、候補のパスワードをハッシュして、一度にすべてのエントリに対してテストすることができなくなりました。各ユーザーのハッシュについてテストするために、特定のパスワードを再計算する必要があります。個別のターゲットハッシュエントリの数が増えるにつれて、ソルトによってもたらされるこの利点は直線的に増えます。もちろん、以下で説明するように、単一のハッシュだけで保護が必要な場合でも、ソルトは依然として重要です。

  2. Rainbowテーブルを実行不可能にしますRainbow table は、パスワードとハッシュを照合する高度に最適化された事前計算テーブルです。それらは巨大なルックアップテーブルよりも少ないスペースを使用しますが、(aspace-time trade-off)を生成するには多くの時間がかかります。 Rainbowテーブルが機能するためには、特定のパスワードが常に同じハッシュに解決される必要があります。ソルトはこの仮定を破り、レインボーテーブルは考えられるソルトごとに新しいエントリを持つ必要があるため、実用的ではありません。

  3. これにより、少なくともハッシュが本当にランダムな場合は、Rainbowテーブルによるtargeted事前計算が防止されます。攻撃者は、ハッシュ(およびソルト)を手にした後でのみ攻撃を開始できます。ソルトがすでにpublicであるがハッシュがそうでない場合、その特定のソルトのレインボーテーブルを生成することで攻撃を最適化できます。これが、WPA2が醜いプロトコルである理由の1つです。ソルトはESSID(ネットワーク名)なので、誰かがターゲットのルーターに対して 攻撃を開始する ができるようになる前に、4ウェイハンドシェイクを手に入れることさえあります。

では、この値が公開され、安全でなく、再利用されている場合、ハッシュする前に値を連結するとどのような利点があるでしょうか?攻撃者に詳細情報を求める必要はありません。それは塩の安全性を高めません。パスワードが複雑になることはありません。メリットはありません。

適切なパスワードハッシュ

だから セキュリティを強化するために何をすべきか ?単一のハッシュの代わりにPBKDF2、bcrypt、scrypt、argon2などの [〜#〜] kdf [〜#〜] を使用できます。 pepper を追加できます。これは、データベースの外部に保存され、パスワードとソルトに追加されるランダムなグローバル値です。単純にダンプするのではなく、ペッパーを盗んでハッシュを攻撃する必要があります。 SQLiを使用したデータベース。

編集:一部のコメントが指摘しているように、ユーザー名がミックスに追加するのに有益である、1つの不自然なシナリオがあります。そのシナリオは、ソルトが実際にはソルトではないほど実装がひどく壊れており、ユーザー名がデータベース内の唯一のユーザー固有の値またはユーザー固有の値である場合です。この場合、ユーザー名を混在させる方が適切です。何よりも。しかし実際には、ソルトがない場合は、ユーザー名を使用する代わりに、oneの使用を開始する必要があります。本当のセキュリティを使用し、ユーザーの安全が確保されているときは、頭を悩ませないでください。

*この文脈では、私はセキュリティシアターを、実際にはセキュリティを意味のある方法で改善するのではなく、より優れたセキュリティの幻想を提供するためだけに存在するセキュリティ対策を実施する慣行として定義しています。

152
forest

塩が想定されているほど十分にランダムである場合、ユーザー名を追加しても保護は追加されません。しかし、それはまた、バグの可能性を増加させるコードをより複雑にすることを除いて、いかなる害も引き起こしません。コードレビューを行う場合、開発者が自分のしていることを完全に理解していないという兆候と見なされる場合があります。

代わりにソルトがほとんど静的であるか、パスワードから派生している場合は、ユーザー名が役立つ場合があります。この場合、ユーザー名は基本的にソルトが提供できなかった目的を果たします。ただし、saltはランダムであることが想定されており、ユーザー名はランダムではないため、これは推奨される方法ではありません。

を参照してくださいhash(username_str + password_str)のようなパスワードをハッシュするとき、ユーザーのユーザー名をソルトとして使用することは良い考えですか?saltとして何を使用する必要がありますか? 良い塩とは何か、ユーザー名が良い塩ではない理由についての詳細な説明。 ソルトハッシュのパスワード保存の安全性が高い理由 も参照して、ソルトの目的を最初に理解してください。

11
Steffen Ullrich

@Damien_The_Unbelieverがコメントで指摘したように、システムが部分的に侵害されたシナリオでのユーザーの偽装を防ぐことができます。

次のシナリオを想像してみてください。どういうわけか、攻撃者は、おそらくSQLインジェクション攻撃を介して、ユーザー名、パスワードハッシュ、ソルトを含む、ログインに使用されるdbテーブルへの読み取り/書き込みアクセスを取得しました。この攻撃者はシステムに他の昇格されたアクセス権はありませんが、追跡不可能な(または追跡が困難な)方法でシステムのユーザーを偽装しようとしています。

  • まず、攻撃者は被害者のアカウントの元のパスワードハッシュとソルトを記録します。
  • 次に、攻撃者は自分のアカウントを登録し、パスワードハッシュとソルトを自分のアカウントから被害者のアカウントにコピーします。
  • これで、攻撃者は自分のパスワードを使用して被害者のアカウントにログインできます。
  • 攻撃者が完了すると、攻撃者は被害者のパスワードハッシュとソルトを復元し、被害者が何が起こったかを認識するのを困難にします。

一部のシステムでは、このレベルのアクセス権を持つ攻撃者は、被害者のユーザー名、salt、および任意のパスワードを使用して(自分のパスワードをコピーするのではなく)単純にパスワードハッシュを生成できますが、システムが pepper saltに加えて(パスワードハッシュおよびsaltとは異なる場所に格納されるすべてのパスワードハッシュの入力に追加されるグローバルに定義された定数)。コショウを危険にさらすことなく、このシナリオの攻撃者が被害者の既知のパスワードを使用してパスワードハッシュを設定できる唯一の方法は、上記のようにシステムによって生成されたパスワードをコピーすることです。

二要素認証はおそらくこれを防ぐことさえできないことに注意してください。攻撃者がユーザーの2FA初期化コード(おそらく同じdbテーブルに格納されている)にもアクセスできる場合、攻撃者のコードが一時的に被害者のアカウントにも書き込まれる可能性があります。

対照的に、パスワードハッシュの計算にユーザー名が使用されている場合、この特定のなりすまし攻撃は機能しません。攻撃者が自分のパスワードハッシュ、salt、および2FAコードを被害者のアカウントにコピーした後でも、被害者のアカウントで攻撃者のパスワードを使用しても、攻撃者のアカウントと同じパスワードハッシュにはならず、ログインは失敗します。

もちろん、このシナリオでは、複雑さが増し、バグの可能性に見合う価値があるかどうかについては議論の余地があります。このシナリオでは、攻撃者はシステムをすでに深刻に侵害している必要があり、このシナリオでは攻撃者はユーザーの実際のパスワードを回復しません。しかし、これは追加の保護であると主張することができます。

10
zacronos

まず、パスワードハッシュは、bcrypt、Argon2、scrypt、PBKDF2などの専用のパスワードハッシュ関数を使用して行う必要があります。その場合、プリミティブのようにソルトとパスワードを連結する必要はありません。関数はそれらを個別の引数として受け取ります。このトピックに関するこのサイトでのトップQ&Aの1つである "パスワードを安全にハッシュする方法?" を参照してください。

パスワードハッシュにおけるソルトの目的は、各パスワードエントリに適用されるハッシュ関数をrandomizeすることです。したがって、現代の特殊なパスワードハッシュに一般的に適用される3つのルールは次のとおりです。

  1. 新しいパスワードを登録するたびに、新しいソルトを生成する必要があります。 (これは、ユーザーがパスワードを変更する場合、古いパスワードにソルトを再利用するのではなく、そのパスワードに新しいソルトを生成する必要があることを意味します。ソルトはパスワードデータベースエントリの状態にバインドされます、ユーザーではありません。)
  2. ソルトの値は、パスワード自体とは無関係である必要があります。パスワードの知識はソルトを推測するのに役立たないはずです、さもなければ、攻撃者はそのような知識を使って攻撃テーブルを事前に計算する可能性があります。ここでの明らかな例は、パスワード自体をソルトとして使用しないことです。 (これは、思ったよりも馬鹿げています。この場合、ソルトをパスワードに沿って保存することはありませんが、それはis馬鹿です。同じパスワードは同じハッシュを持ちます。)
  3. 2つの塩が互いに等しい可能性は非常に低いはずです。できれば、アプリケーション内だけでなく、全世界でグローバルに使用することをお勧めします。

今、私たちはこれらの観察をすることによってあなたの質問に答えることができます:

  • 十分な数のランダムバイトで構成されたソルトは、すでにこれらの基準を満たしています。 (暗号的に強力な乱数ジェネレーターからの)16個のランダムなバイトは賢明に聞こえます。
  • ユーザー名をそのようなランダムなソルトに連結しても効果はありません。
  • たとえば、新しいパスワードを登録するたびにインクリメントされるカウンターとして、またはタイムスタンプとして、ソルトが予測可能であるが幾分繰り返されない方法で生成されている場合、ユーザー名やサイトのドメイン名などの追加の値を連結すると、それはよりユニークです。ただし、ランダムなソルトを使用する方が簡単です(永続的なカウンター状態を追跡する必要はありません)。
6
Luis Casillas