bcryptに組み込みのソルトを含めるにはどうすればよいですか


617

Coda Haleの記事「パスワードを安全に保存する方法」では、次のように述べています。

bcryptには、レインボーテーブル攻撃を防ぐためのソルトが組み込まれています。

彼はこの論文を引用し、OpenBSDの実装ではbcrypt次のように述べています。

OpenBSDは、カーネルがデバイスタイミングから収集するランダムデータをシードしたarcfour(arc4random(3))キーストリームから128ビットのbcryptソルトを生成します。

これがどのように機能するかわかりません。私の塩の概念では:

  • 保存されているパスワードごとに異なる必要があるため、それぞれに個別のレインボーテーブルを生成する必要があります。
  • 繰り返し使用できるように、どこかに保存する必要があります。ユーザーがログインしようとすると、パスワードの試行が行われ、最初にパスワードを保存したときと同じソルトアンドハッシュ手順を繰り返し、比較します。

Devcrypt(Railsログインマネージャー)をbcryptで使用している場合、データベースにsalt列がないため、混乱しています。ソルトがランダムでどこにも保存されていない場合、ハッシュプロセスを確実に繰り返すにはどうすればよいですか?

要するに、どのようにbcryptは組み込みのソルトを持つことができますか?

回答:


789

これはbcryptです:

ランダムな塩を生成します。「コスト」要素は事前設定されています。パスワードを収集します。

saltとコスト係数を使用して、パスワードから暗号化キーを取得します。これを使用して、既知の文字列を暗号化します。保管コスト、塩、および暗号文を。これら3つの要素は長さがわかっているため、それらを連結して1つのフィールドに格納するのは簡単ですが、後で分割することもできます。

誰かが認証を試みたときに、保存されているコストとソルトを取得します。入力パスワード、コスト、ソルトからキーを導出します。同じ既知の文字列を暗号化します。生成された暗号文が保存されている暗号文と一致する場合、パスワードは一致しています。

Bcryptは、PBKDF2などのアルゴリズムに基づく従来のスキームと非常によく似た方法で動作します。主な違いは、既知のプレーンテキストを暗号化するための派生キーの使用です。他のスキームは、(合理的に)キー導出関数が不可逆であると想定し、導出されたキーを直接格納します。


データベースに格納されるbcrypt「ハッシュ」は次のようになります。

$ 2a $ 10 $ vI8aWBnW3fID.ZQ4 / zo1G.q1lRps.9cGLcZEiGDMVr5yUP1KUOYTa

これは実際には「$」で区切られた3つのフィールドです。

  • 2a使用されたbcryptアルゴリズムのバージョンを識別します。
  • 10コスト要因です。2 キー導出関数の10回の反復が使用されます(ちなみに、これでは不十分です。コストは12以上にすることをお勧めします)。
  • vI8aWBnW3fID.ZQ4/zo1G.q1lRps.9cGLcZEiGDMVr5yUP1KUOYTa修正されたBase-64で連結およびエンコードされたソルトと暗号テキストです。最初の22文字はソルトの16バイト値にデコードされます。残りの文字は、認証のために比較される暗号テキストです。

この例は、Coda Haleのルビー実装のドキュメントから抜粋したものです。


7
コストファクター10では十分ではない理由について詳しく教えてください。Grailsでは、bcryptのコストファクター/ログラウンドのデフォルト値は10であるため、提案を踏まえて更新する価値があるかもしれません。
pm_labs、2012年

57
bcryptのコスト係数は指数関数的です。つまり、コスト係数10は2 ^ 10ラウンド(1024)を意味し、コスト係数16は2 ^ 16ラウンド(65536)を意味します。5-10秒かかるのは当然です。コスト係数が10の場合の約64倍の時間がかかります。他の誤った情報を取り除くために、PHPのcrypt関数は、cで実装されているunix cryptライブラリを使用します。
thomasrutter

3
@TJChambersそうです。アカウントにパスワードを設定できる場合は、認証することができます。パスワードハッシュは、その攻撃を防ぐことを目的としていません。これは、パスワードテーブルへの読み取り専用アクセス権を持つ攻撃者が認証されないようにするためのものです。たとえば、テーブルが入ったバックアップテープを入手したとします。
エリクソン2014

8
@LobsterManいいえ、そうではありません。秘密を守ることができれば、このアプローチは使用せず、パスワードを保存するだけです。パスワード認証スキームは、攻撃者があなたが知っているすべてのものを発見したという想定に基づいています。ソルトは、各パスワードを個別に攻撃することを要求するためにあります。パスワードのテストに必要な計算量は、反復によって制御されます。ユーザーが適切なパスワードを選択すれば、ソルトが明らかになったとしても安全です。saltを非表示にすると、不正なパスワードを使用しているユーザーに役立つ場合がありますが、私は最初にパスワードの品質に取り組みます。
エリクソン2018年

1
@NLVそれはbcryptの仕様で定義された文字列です:"OrpheanBeholderScryDoubt"
エリクソン

182

私はそのフレーズは次のように書かれるべきだったと思います:

bcryptには、レインボーテーブル攻撃を防ぐために、生成されたハッシュ組み込まれたソルトがあります

bcryptユーティリティ自体は塩のリストを維持するためには表示されません。むしろ、ソルトはランダムに生成され、関数の出力に追加されるため、後で記憶されます(Javaの実装bcryptに従って)。言い換えれば、によって生成される「ハッシュ」はハッシュだけでbcryptはありません。むしろ、ハッシュソルトを連結したものです。


20
OK、それで私はサイトにサインアップし、パスワード「foo」を選びます。Bcrypt「akd2!*」のランダムなソルトを追加し、「fooakd2!*」をハッシュして保存します。後で、パスワード「bar」を使用してサインインを試みます。私が正しいかどうかを確認するには、「barakd2!*」をハッシュする必要があります。ソルトが最初にランダムに生成された場合、ハッシュして比較する前に「bar」に戻す方法をどのようにして知るのでしょうか。
ネイサンロング

46
@Nathan:bcrypt生成された出力(データベースに格納されている)からソルトを抽出する方法を知っています。認証の時期になるbcryptと、元の出力をハッシュコンポーネントとソルトコンポーネントに分離します。saltコンポーネントは、ユーザーが入力した入力パスワードに適用されます。
Adam Paynter、2011

22
ネイサンロングのコメントに答えるために、これについての良い考え方は、塩は秘密にされるべきではないということです。これが、上記で指摘した答えの1つとして、saltがbcrypt関数の出力に含まれている理由です。saltは、共通のパスワードのリストであるレインボーテーブル、または単なるパスワードでありながらハッシュ化されたブルートフォースなどを防ぐためにあります。ソルトがない場合、データベースAのパスワードのハッシュは、データベースBのパスワードのハッシュと同じになります。ソルトはハッシュ値を変更するだけで、データベースを盗んだユーザーがパスワードを解読(ハッシュ解除)することを困難にします。
ジョセフアストラハン、2016年

11
@Nathanですが、攻撃者はすべてのパスワードから既知のソルトを削除して、それらを使用してテーブルを作成できますか?
オスカー

3
これが私が理解する方法です。アイデアは、すべてのパスワードに固有のソルトがあるということです。ソルトはパスワードハッシュに組み込まれているため、ハッカーはすべてのパスワードに対してレインボーテーブルを作成する必要があります。中程度のデータベースの場合、これには膨大な時間がかかります。それはすべて、攻撃者の速度を落とし、総当たり攻撃を無意味にすることです。
PVermeer

0

物事をより明確にするために、

登録/ログイン方向->

パスワード+ saltは、コスト、ソルト、パスワードから生成されるキーで暗号化されます。その暗号化された値をと呼びますcipher text。次に、この値にソルトを付加し、base64を使用してエンコードします。それにコストをアタッチすると、これはから生成された文字列ですbcrypt

$2a$COST$BASE64

この値は最終的に保存されます。

パスワードを見つけるために攻撃者は何をする必要がありますか?(他の方向<-)

攻撃者がDBを制御できるようになった場合、攻撃者はbase64値を簡単にデコードして、塩を見ることができます。塩は秘密ではありません。それはランダムですが。次に、彼はを復号化する必要がありcipher textます。

さらに重要なこと:このプロセスにはハッシュ処理はなく、CPUに負荷のかかる暗号化-復号化が行われます。したがって、ここではレインボーテーブルの関連性は低くなります。


-2

これは、Spring SecurityのPasswordEncoderインターフェースドキュメントからのものです。

 * @param rawPassword the raw password to encode and match
 * @param encodedPassword the encoded password from storage to compare with
 * @return true if the raw password, after encoding, matches the encoded password from
 * storage
 */
boolean matches(CharSequence rawPassword, String encodedPassword);

つまり、ユーザーが次のログイン時に再度入力するrawPasswordと、前回のログイン/登録時にデータベースに保存されているBcryptエンコードされたパスワードを照合する必要があります。


これは質問にまったく答えません... bcryptが組み込みのソルトをどのように持つことができるかについては何も述べていません
spencer.sm
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.