32

パスワードをハッシュするためにC#とBCrypt.Netを使用しています。

例えば:

string salt = BCrypt.Net.BCrypt.GenerateSalt(6);
var hashedPassword = BCrypt.Net.BCrypt.HashPassword("password", salt);

//This evaluates to True. How? I'm not telling it the salt anywhere, nor
//is it a member of a BCrypt instance because there IS NO BCRYPT INSTANCE.
Console.WriteLine(BCrypt.Net.BCrypt.Verify("password", hashedPassword));
Console.WriteLine(hashedPassword);

ソルトをどこにも保存していない場合、BCryptはハッシュを使用してパスワードをどのように検証しますか?私が持っている唯一のアイデアは、ハッシュの最後にソルトを追加することです。

これは正しい仮定ですか?

4

2 に答える 2

105

BCryptハッシュ文字列は次のようになります。

$2a$10$Ro0CUfOqk6cXEKf3dyaM7OhSCvnwM9s4wIX9JeLapehKK5YdLxKcm
\__/\/ \____________________/\_____________________________/
 |   |        Salt                     Hash
 |  Cost
Version

どこ

  • 2a:アルゴリズム識別子(BCrypt、UTF8でエンコードされたパスワード、nullで終了)
  • 10:コストファクター(2 10= 1,024ラウンド)
  • Ro0CUfOqk6cXEKf3dyaM7O:OpenBSD-Base64でエンコードされたソルト(22文字、16バイト)
  • hSCvnwM9s4wIX9JeLapehKK5YdLxKcm:OpenBSD-Base64でエンコードされたハッシュ(31文字、24バイト)

編集:私はこれらの単語が正確に適合することに気づきました。私は共有しなければなりませんでした:

$2a$10$TwentytwocharactersaltThirtyonecharacterspasswordhash
$==$==$======================-------------------------------

BCrypt、16バイトのソルトを使用して24バイトのバイナリハッシュを作成します。バイナリハッシュとソルトは好きなように自由に保存できます。それを文字列にbase-64エンコードする必要があるとは何も言われていません。

しかし、BCryptはOpenBSDに取り組んでいた人たちによって作成されました。OpenBSDは、パスワードファイルのフォーマットをすでに定義しています。

$ [HashAlgorithmIdentifier]$[AlgorithmSpecificData]

これは、「bcrypt仕様」がOpenBSDパスワードファイル形式に容赦なくリンクされていることを意味します。そして、誰かが「bcryptハッシュ」を作成するときはいつでも、それを次の形式のISO-8859-1文字列に変換します。

$ 2a$ [Cost]$[Base64Salt][Base64Hash]

いくつかの重要なポイント:

  • 2aアルゴリズム識別子です

    • 1:MD5
    • 2:どのエンコーディングパスワードが含まれているかについて混乱していた初期のbcrypt(廃止)
    • 2a:現在のbcrypt。パスワードをUTF-8でエンコードされたものとして指定します
  • コストは、ハッシュを計算するときに使用されるコスト係数です。「現在の」値は10です。これは、内部キーの設定が1,024ラウンドを通過することを意味します。

    • 10:2 10 =1,024回の反復
    • 11:2 11 =2,048回の反復
    • 12:2 12 =4,096回の反復
  • OpenBSDパスワードファイルで使用されるbase64アルゴリズムは、他の人が使用するBase64エンコーディングと同じではありません。彼らは独自のものを持っています:

      Regular Base64 Alphabet: ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/
          BSD Base64 Alphabet: ./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789
    

    したがって、bcryptの実装では、組み込みまたは標準のbase64ライブラリを使用できません。


この知識を身に付ければcorrectbatteryhorsestapler、保存されたハッシュに対してパスワードを確認できるようになります。

$2a$12$mACnM5lzNigHMaf7O1py1O3vlf6.BA8k8x3IoJ.Tq3IB/2e7g61Km

BCryptバリアント

bcryptのバージョンには多くの混乱があります。

$ 2 $

BCryptはOpenBSDの人々によって設計されました。これは、OpenBSDパスワードファイルに保存するためにパスワードをハッシュするように設計されています。ハッシュ化されたパスワードは、使用されるアルゴリズムを識別するためにプレフィックスとともに保存されます。BCryptはプレフィックスを取得しました$2$

これは、他のアルゴリズムプレフィックスとは対照的でした。

  • $1$:MD5
  • $5$:SHA-256
  • $6$:SHA-512

$ 2a $

元のBCrypt仕様では、非ASCII文字の処理方法やヌルターミネータの処理方法が定義されていませんでした。仕様が改訂され、文字列をハッシュするときに次のように指定されました。

  • 文字列はUTF-8でエンコードされている必要があります
  • ヌルターミネータを含める必要があります

$ 2x $、$ 2y $ (2011年6月)

crypt_blowfishでバグが発見されました、BCryptのPHP実装。8番目のビットが設定された文字の取り扱いを誤っていました。

彼らは、システム管理者が既存のパスワードデータベースを更新し、に置き換え$2a$$2x$、それらのハッシュが不良であることを示すことを提案しました(そして古い壊れたアルゴリズムを使用する必要があります)。$2y$彼らはまた、固定アルゴリズムによって生成されたハッシュに対してcrypt_blowfishを放出させるというアイデアを提案しました。正規のOpenBSDを含め、他の誰も2x/のアイデアを採用しませんでし2yた。このバージョンマーカーはcrypt_blowfishに限定されていました

バージョン$2x$および$2y$は、 $2a$よりも「優れている」または「強力」ではありません。それらはBCryptの1つの特定のバグのある実装の残骸です。

20億ドル (2014年2月)

BCryptのOpenBSD実装にバグが発見されました。彼らは、サポート文字列を持たない言語で実装を記述しました。そのため、長さプレフィックス、文字へのポインターを使用して実装を偽造し、そのポインターに。を使用してインデックスを付けていました[]。残念ながら、文字列の長さをに格納していましたunsigned char。パスワードが255文字より長い場合、パスワードはオーバーフローして255で折り返されます。BCryptはOpenBSD用に作成されました。彼らが彼らのライブラリにバグを持っているとき、彼らはバージョンをぶつけても大丈夫だと決めました。これは、 「自分の」仕様を最新の状態に保ちたい場合は、他のすべての人がそれに従わなければならないことを意味します。


2a2x2y、および2bの間に違いはありません。実装を正しく記述した場合、それらはすべて同じ結果を出力します。

  • 最初から正しいことを行っていた場合(utf8に文字列を格納し、nullターミネータをハッシュする) 、2、2a2x2y、および2bの間に違いはありませ。実装を正しく記述した場合、それらはすべて同じ結果を出力します。
  • バージョン$2b$は、 $2a$よりも「優れている」または「強力」ではありません。これは、BCryptの特定のバグのある実装の残骸です。しかし、BCryptは正規にOpenBSDに属しているので、バージョンマーカーを好きなように変更することができます。
  • バージョン$2x$$2y$は、何よりも優れているわけではなく、さらには好ましいものでもありません。それらはバグのある実装の残骸であり、簡単に忘れてしまうはずです。

2xと2yを気にする必要があるのは、2011年にcrypt_blowfishを使用していた可能性のある人だけです。2bを気にする必要があるのは、OpenBSDを実行している可能性のある人だけです。

他のすべての正しい実装は同一で正しいものです。

于 2012-06-07T14:06:46.333 に答える
24

ソルトをどこにも保存していない場合、BCryptはハッシュを使用してパスワードをどのように検証しますか?

明らかにそれはそのようなことをしていません。塩はどこかに保存する必要があります。

ウィキペディアでパスワード暗号化スキームを調べてみましょう。http://en.wikipedia.org/wiki/Crypt_(Unix)から:

関数の出力は単なるハッシュではありません。これは、ソルトをエンコードし、使用されるハッシュアルゴリズムを識別するテキスト文字列です。

または、このテーマに関する以前の質問への回答には、ソースコードへのリンクが含まれていました。ソースコードの関連セクションは次のとおりです。

    StringBuilder rs = new StringBuilder();
    rs.Append("$2");
    if (minor >= 'a') {
        rs.Append(minor);
    }
    rs.Append('$');
    if (rounds < 10) {
        rs.Append('0');
    }
    rs.Append(rounds);
    rs.Append('$');
    rs.Append(EncodeBase64(saltBytes, saltBytes.Length));
    rs.Append(EncodeBase64(hashed,(bf_crypt_ciphertext.Length * 4) - 1));
    return rs.ToString();

明らかに返される文字列は、バージョン情報、使用されたラウンド数、base64としてエンコードされたソルト、base64としてエンコードされたハッシュです。

于 2011-03-22T15:48:05.770 に答える