web-dev-qa-db-ja.com

ソルトハッシュがパスワードの保存に対してより安全なのはなぜですか

私はソルトハッシュについて多くの議論があることを知っています。その目的は、すべての可能なハッシュ(通常は最大7文字)のレインボーテーブルを作成することを不可能にすることであることを理解しています。

私の理解では、ランダムなソルト値は単にパスワードハッシュに連結されるだけです。レインボーテーブルをパスワードハッシュに対して使用できず、ランダムソルトハッシュであることがわかっている最初のXビットを無視できるのはなぜですか?

更新

返信ありがとうございます。これが機能するには、ディレクトリ(LDAPなど)が各ユーザーに固有のソルトを格納する必要があるか、ソルトが「失われ」、認証が発生しないように思われます。

250
Tsyras

通常は次のように機能します。

あなたのパスワードが「野球」だとしましょう。私はそれをそのまま保存することもできますが、私のデータベースを取得する人はだれでもパスワードを取得します。したがって、代わりにSHA1ハッシュを実行して、これを取得します。

$ echo -n baseball | sha1sum
a2c901c8c6dea98958c219f6f2d038c44dc5d362

理論的には、SHA1ハッシュを元に戻すことは不可能です。しかし その正確な文字列でのグーグル検索 を行えば、元のパスワードを回復するのに問題はありません。

さらに、データベース内の2人のユーザーが同じパスワードを持っている場合、それらは同じSHA1ハッシュを持ちます。そして、そのうちの1つにtry "baseball"という パスワードヒント がある場合---さて、私は両方のユーザーのパスワードを知っています。

したがって、ハッシュする前に、一意の文字列を付加します。 秘密ではなく、ただユニークなもの。 WquZ012Cはどうですか。これで、文字列WquZ012Cbaseballをハッシュしています。それはこれへのハッシュです:

c5e635ec235a51e89f6ed7d4857afe58663d54f5

その文字列をグーグルしても何も起こらない(おそらくthisページを除く)ので、今は何かに取り掛かっています。また、person2もパスワードとして「野球」を使用している場合は、別のソルトを使用して別のハッシュを取得します。

もちろん、パスワードをテストするには、塩が何であるかを知っている必要があります。それをどこかに保存する必要があります。ほとんどの実装では、ハッシュを使用して、通常はデリミタを使用してハッシュを直接処理します。 opensslがインストールされている場合は、これを試してください。

[tylerl ~]$ openssl passwd -1
Password: baseball
Verifying - Password: baseball
$1$oaagVya9$NMvf1IyubxEYvrZTRSLgk0

これにより、標準のcryptライブラリを使用したハッシュが得られます。したがって、ハッシュは$1$oaagVya9$NMvf1IyubxEYvrZTRSLgk0です。実際には、$で区切られた3つのセクションです。視覚的にわかりやすくするために、区切り文字をスペースに置き換えます。

$1$oaagVya9$NMvf1IyubxEYvrZTRSLgk0
 1 oaagVya9 NMvf1IyubxEYvrZTRSLgk0
  • 1は、「アルゴリズム番号1」を意味します 少し複雑です ですが、MD5を使用します。 はるかに優れている他のたくさん がありますが、これは私たちの例です。
  • oaagVya9は私たちの塩です。私たちのハッシュですぐそこに潜入しました。
  • NMvf1IyubxEYvrZTRSLgk0は、base64でエンコードされた実際のMD5合計です。

プロセスを再度実行すると、異なるソルトで完全に異なるハッシュが得られます。この例では、約1014 この1つのパスワードを保存する方法。これらはすべて「baseball」というパスワード用です。

$1$9XsNo9.P$kTPuyvrHqsJJuCci3zLwL.
$1$nLEOCtx6$uSnz6PF8q3YuUhB3rLTC3/
$1$/jZJXTF3$OqDuk8T/cEIGpeKWfsamf.
$1$2lC.Cb/U$KR0jkhpeb1sz.UIqvfYOR.

しかし、チェックしたいソルトを意図的に指定すると、期待した結果が返されます。

[tylerl ~]$ openssl passwd -1 -salt oaagVya9
Password: baseball
Verifying - Password: baseball
$1$oaagVya9$NMvf1IyubxEYvrZTRSLgk0

これが、パスワードが正しいかどうかを確認するために実行するテストです。ユーザーの保存されたハッシュを見つけ、保存されたソルトを見つけ、保存されたソルトを使用して同じハッシュを再実行し、結果が元のハッシュと一致するかどうかを確認します。

これを自分で実装する

明らかに、この投稿は実装ガイドではありません。 MD5を単純に塩漬けにしないでください。今日のリスク環境ではそれだけでは不十分です。代わりに、ハッシュ関数を数千回実行するiterativeプロセスを実行する必要があります。これは 他の場所で説明されています 何度も繰り返されているため、ここでは "なぜ"については説明しません。

これを行うには、いくつかの確立された信頼できるオプションがあります。

  • crypt:上記で使用した関数は、unix cryptパスワードハッシュメカニズムの古いバリエーションです。すべてのUnix/Linuxオペレーティングシステムに。元の(DESベースの)バージョンは恐ろしく安全ではありません。それさえ考慮しないでください。私が示したもの(MD5ベース)はより良いですが、それでも今日は使用すべきではありません。 SHA-256およびSHA-512のバリエーションを含む、その後のバリエーションは妥当なはずです。最近の亜種はすべて、複数ラウンドのハッシュを実装しています。

  • bcrypt:上記のcrypt関数呼び出しのフグのバージョン。 blowfishには非常に高価なキーセットアッププロセスがあるという事実を利用し、それに応じてキーセットアップ時間を増加させる「コスト」パラメーターを使用します。

  • PBKDF2:( "Password-based Key Derivation Function version 2")単純なパスワードから強力な暗号鍵を生成するために作成され、これ 実際にはRFCがある である、ここにリストされている唯一の関数です。設定可能な数のラウンドを実行し、各ラウンドでパスワードと前のラウンドの結果をハッシュします。最初のラウンドは塩を使用します。本来の目的は強力なキーを作成することであり、パスワードを保存することではありませんが、目標の重複によりこれはうまくいきます-ここでも信頼できるソリューション。利用可能なライブラリがなく、何かを最初から実装せざるを得なかった場合、これが最も簡単で最も文書化されたオプションです。しかし、もちろん、入念に吟味されたライブラリを使用することが常に最善です。

  • scrypt:専用ハードウェアに実装するのが難しいように特別に設計された、最近導入されたシステム。 scryptは、ハッシュ関数の複数ラウンドを必要とするだけでなく、非常に大きな作業メモリ状態を持ち、RAM非常に新しく、ほとんどが証明されていませんが、少なくとも他のセキュリティと同じくらい安全に見え、おそらくそれらすべての中で最も安全です。

402
tylerl

技術的には、レインボーテーブルを使用してソルトハッシュを攻撃できます。しかし、技術的にのみ。ソルトハッシュは、クリプトマジックを追加するのではなく、衝突を正常に検出するために必要なRainbowテーブルのサイズを指数関数的に増加させることによって、Rainbowテーブルの攻撃を無効にします。

そして、はい、塩を保存する必要があります:)

49
xkcd

ハッシュの後には追加されません。ハッシュの前に追加されるため、ハッシュはソルトごとに完全に異なります。

そうではない

hash abcd = defg
store 123defg (for user with 123 salt) and 456defg (for user with 456 salt)  

です

hash 123abcd = ghij 
hash 456abcd = klmn
23
AJ Henderson

パスワードハッシュの場合、 PBKDF2/RFC2898/PKCS5v2 、Bcrypt、またはScryptのようなものを使用する必要があります。これらはすべて、1回ではなく反復回数(「作業係数」)を選択できます。 。たとえばPBKDF2は、内部的に [〜#〜] hmac [〜#〜] 既知のハッシュアルゴリズム(通常はSHA-512、SHA-256、またはSHA-1)を使用したキー付きハッシュを使用します。繰り返しの数、できれば数十から数十万の高さで、基本的にユーザーが文句を言わずにそれを保つことができます。

反復回数が多い理由は、攻撃者の速度を落とすことです。これにより、攻撃者が一定期間に通過できるキースペースが減少し、より適切なパスワードを効果的に攻撃できなくなります。 「パスワード」と「P @ $$ w0rd」は、明らかにオフライン攻撃に関係なくクラックされることになります。

正解です。各行(ユーザー)には、独自に生成された、暗号的にランダムな長いソルトが必要です。そのソルトはプレーンテキスト形式で保存されます。

PBKDF2、Bcrypt、またはScryptでは、反復回数(作業係数)もデータベースにプレーンテキストで保存することをお勧めします。これにより、変更が簡単になります(個人的には、いくぶんランダム化された反復回数を使用します-常に異なる場合) 、その後、「ああ、いいえ、小さな変更ですべてが台無しになる可能性があります-これ以上変更しないでください」という恐れは、経営陣や他の開発者からはありません)

パスワードハッシュにPBKDF2を使用する場合は、ネイティブハッシュ出力よりも大きな出力を要求しないでください。SHA-1の場合は20バイト、SHA-512の場合は64バイトです。

2つの異なる塩を使用したPBKDF2の実際の例を示します。

(PBKDF2 HMACパスワードソルト反復出力バイトの結果)

  • PBKDF2 HMAC-SHA-512 MyPass vA8u3v4qzCdb 131072 64
    • 2e3259bece6992f012966cbf5803103fdea7957ac20f3ec305d62994a3f4f088f26cc3889053fb59a4e3c282f55e9179695609ee1147cffae1455880993ef874
  • PBKDF2 HMAC-SHA-512 MyPass l6eZQVf7J65S 131072 64
    • 1018ad648096f7814bc2786972eb4091f6c36761a8262183c24b0f4d34abb48073ed2541ee273220915638b46ec14dfb2b23ad64c4aa12f97158340bdc12fc57
15

タイラーが言ったことに加えて、現代の暗号では、塩はレインボーテーブルからの保護に使用されていないことを強調することが重要です。 Rainbowテーブルを実際に使用する人はいません。本当の危険は並列化です:

  • ソルトがないか、またはstaticソルトのみで、100万ハッシュのDBを盗んだ場合、すべてのコアをブルートフォーシングで投げることができます。各コアは、100万のハッシュを同時に攻撃します。パスワードとして使用される文字列のanyをヒットするたびに、ハッシュの一致が表示されます。したがって、検索スペースを使い尽くす必要があります一度ミリオンパスワードを解読するために。これは、完全に現実的なパスワードリークのスケールであり、数十個のGPUをカスタムFPGAに投げるだけの魅力的なターゲットです。検索スペースの下位30%しか使い果たしていなくても、500k程度の実世界のパスワードを使用することはできます。これらのパスワードは私の辞書を作成するために使用されるため、次回誰かがハッシュDBをリークしたときに、そのうちの90%を数時間で解読します。
  • 代わりに、すべてのパスワードが独自の一意のソルトでハッシュされる場合、同じパスワードでもまったく異なるハッシュが格納されるため、それらすべてを個別に攻撃する必要があります。つまり、私はおそらく、電力を大量に消費する高価なGPU/FPGAファームを使用して、いくつかの高価値のターゲットを攻撃する可能性があります(たとえば、オバマがユーザーである場合、彼のパスワードを取得しても費用はかかるはずです)。それから無料で数十万のパスワード。 millionパスワード全体を取得したい場合は、完全な総当たり検索をmillion回実行する必要があります。

そして、それが静的であるかどうかに関係なく、ソルトが広く使用されていない限り、準備されたRainbowテーブルからあなたを保護しますが、ユニークなハッシュごとのソルトのみが、多くの並列計算能力を使用してすべてをクラックするという本当の危険からあなたを保護しますすぐに。

14
mathrick

複数のユーザーが同じパスワードを持っている場合、ハッシュが異なるため、より安全です。

Pythonを使用した簡単な実装:

import hashlib

passwordA = '123456'
passwordB = '123456'

hashlib.md5(passwordA).hexdigest()
'e10adc3949ba59abbe56e057f20f883e'

hashlib.md5(passwordB).hexdigest()
'e10adc3949ba59abbe56e057f20f883e'

そして塩を加えましょう:

saltA = 'qwerty'
salbB = 'asdfgh'

passA = passwordA + saltA
passB = passwordB + saltB

hashlib.md5(passA).hexdigest()
'086e1b7e1c12ba37cd473670b3a15214'

hashlib.md5(passB).hexdigest()
'dc14768ac9876b3795cbf52c846e6847'

これは非常に簡略化されたバージョンです。塩は好きな場所に追加できます。パスワードの最初/中間/終わり。

特に多くのユーザーがいて、それぞれのハッシュが異なる場合は、Saltは非常に便利です。 FacebookやGoogle、Twitterアカウントなど、100万のアカウントで同じパスワードを使用する確率を想像してみてください。

11
zakiakhmad

まず、2人のユーザーがやや弱いパスワード「野球」を使用している場合、1つのパスワードをクラックしてもソルトのために2番目のパスワードをクラックしてもまったく役に立ちません。塩漬けなしでは、1つの価格で2つのパスワードを解読しました。

次に、レインボーテーブルには、事前に計算されたハッシュが含まれています。そのため、クラッカーは、虹のテーブルで「野球」のハッシュを膨大な数のエントリで検索することができます。レインボーテーブルのガジリオンハッシュを計算するよりもはるかに高速です。そしてそれは塩漬けによって防がれます。

そして今重要なのは、良いパスワードを持っている人もいれば、悪いパスワードを持っている人もいます。 100万人のユーザーがいて、3人が同じパスワードを使用している場合は、knowそのパスワードは脆弱です。ソルトしないと、3つの同一のハッシュになります。したがって、誰かがハッシュのデータベースを盗んだ場合、彼らはすぐにどのユーザーが弱いパスワードを持っているかを確認し、それらの解読に集中できます。 1keOj29fa0romnよりも「野球」をクラックする方がはるかに簡単です。ソルトしないと、弱いパスワードが目立ちます。ソルティングを使用すると、クラッカーはどのパスワードが脆弱で、どのパスワードが解読されにくいかを理解できません。

8
gnasher729

ハッシュがソルトされているかどうかは、攻撃者がパスワードハッシュを持っている場合にのみ違いを生みます。ソルトがなければ、ハッシュはレインボーテーブル(パスワードをハッシュに関連付ける事前計算済みの辞書)で攻撃される可能性があります。 Nビットソルトは、Rainbowテーブルのストレージ要件と、そのテーブルを計算する時間を2 ** N倍に増やします。したがって、たとえば、32ビットのソルトを使用する場合、たとえばpassw0rdというパスワードの1つのRainbowテーブルディクショナリエントリは、数ギガバイトのストレージを必要とするため、現在のストレージハードウェアを使用すると、このようなRainbowテーブルは非常に高価になります。そのため、ソルトが存在する場合、攻撃者は取得した特定のパスワードハッシュセットに対するブルートフォース攻撃に限定されます。

しかしながら:

  • 脆弱なパスワードの場合、ブルートフォース攻撃は比較的短時間で成功します。
  • rainbowテーブルでは、十分に強力なパスワードは見つかりません。
  • 攻撃者がハッシュにアクセスできる場合、システムのセキュリティは既に危険にさらされています。最新のシステムとプロトコルは、パスワードのハッシュを明らかにしません。 攻撃者がパスワードデータベースにアクセスできない場合、パスワードはプレーンテキストで保存されることもあります。
  • 攻撃者がパスワードを逆にするためにハッシュに到達するためにシステムを危険にさらす必要がある場合、攻撃者にとって価値のある唯一のパスワードは、攻撃者がまだアクセスしていない他のセキュリティドメインで再利用されるものだけです。再利用されないパスワードには価値がありません(アカウントに関連付けられている他の機密情報よりも確かに価値が低くなります)。

ユーザーjoewestlakeがパスワードgod1234を使用するとします。攻撃者は、レインボーテーブルを使用して即座にこれを元に戻します。 (または、ハッシュがソルトされている場合、パスワードが非常に悪いため、ブルートフォース攻撃を使用してたった数分で解読できます。)ここで問題は、joewestlakeがGmailアカウントにgod1234も使用したことです、そしてオンラインバンキングのために、おっと!これで攻撃者はジョーの電子メールを読み、ジョーについて十分に学習して、ジョーのオンラインバンキングにログインするときに「最初のペットの名前は何でしたか」という質問に簡単に答えられるようにします。

したがって、ソルトの理論的根拠は、中程度のセキュリティのパスワードを元に戻すのを難しくすることによって、ユーザーを保護することです。ソルトなしでは、合理的になり得るパスワードレインボーテーブルで見つかると予想されますが、個別にブルートフォースするのに非常に長い時間がかかるほど強力です。しかし、ソルトは、ハッシュが危険にさらされた場合にのみこの利点を提供します。これは、すでに重大なセキュリティ違反であり、この利点は、他のシステムで中程度のセキュリティパスワードを再利用するユーザーにのみあります。

代わりに、ジョーは10個のランダムな英数字と記号で構成されるパスワードを使用しました。これはまだRainbowテーブルにある可能性がありますが、多くの作業が必要です。したがって、ジョーがGmailとオンラインバンキングに同じパスワードを使用したとしても、ソルトのおかげで彼は安全です。クラッカーは彼のブルートフォースクラックをおそらく数時間、おそらく数日間実行します。このクラックは、同じシステムの他のユーザーから弱いパスワードを持っている多数の弱いパスワードを生み出します。攻撃者はその利回りに満足し、クラッキングを停止します。 Joeのパスワードは決して取り消されません。

さらに、違反が検出され、ユーザー(ジョーを含む)がパスワードを変更するように助言された場合、ジョーのパスワードを含む中程度のセキュリティパスワードスペース全体を攻撃者が引き続き攻撃していても、ジョーは攻撃者のクラッキングの試行を追い越す可能性があります。 。 Joeは、侵害されたシステムで使用したパスワードは、Gmailと銀行のパスワードとまったく同じであることを知っているため、他の2つを変更するためにスクランブルをかけます。ソルトは、パスワードを再利用するユーザーがパスワードを変更する時間を稼ぐため、ここで役立ちます。 saltは、数分で解読される非常に弱いパスワードを使用するユーザーには役立ちませんが、解読に数時間または数日かかるパスワードのユーザーには、戦いのチャンスがあります。

1
Kaz