web-dev-qa-db-ja.com

PHP crypt()は長すぎるため、ソルトをトリミングします

パスワードハッシュにBlowfishをPHP crypt()で使用していますが、奇妙なことに気づきました。PHPドキュメント:

CRYPT_BLOWFISH-次のソルトを使用したBlowfishハッシュ:「$ 2a $」、「$ 2x $」または「$ 2y $」、2桁のコストパラメータ「$」、およびアルファベット「./0-9Aからの22桁」 -Za-z」。

最終ハッシュに含まれるソルトは、ソルトが長すぎるかのように1文字短い(最後のものが切り取られる)ことに気付きましたが、そうではありません。

スクリプト出力の例:

塩:長さ22の97504ebb48c4619f820f83

ブローフィッシュ:$ 2a $ 13 $ 97504ebb48c4619f820f8u4QTtlV5MoqHt9l7hmK4jEohUXrI.0PK

ハッシュマッチ。

ご覧のとおり、ランダムソルトは正確に22桁ですが、最終ハッシュには「3」がありません。 21文字だけをソルトにすると、ハッシュが破損し、機能しません。では、なぜ最後の文字を切り捨てるのでしょうか?

PHPマニュアル の例でも、ランダムなソルトに最後の$を追加します。理由のためにそこに$あるのか、それともBlowfish、SHA-256、SHA-512にランダムに追加して、みんなを混乱させたのですか?

そして最後に、これは私のコードです:

if (CRYPT_BLOWFISH == 1) {
    $salt = md5(uniqid(Rand(), TRUE));
    $salt = substr($salt, 0, 22);

    echo "Salt: " . $salt . " with length " . strlen($salt) . "<br />";
    $pass = "rasmuslerdorf";

    $bsalt = "$2a$13$".$salt;
    $blowfish=crypt($pass, $bsalt);
    echo 'Blowfish:     ' . $blowfish . "<br />";

    if (crypt($pass, $blowfish) == $blowfish) {
        echo "Hash match.<br />";
    }
    else echo "no<br />";
}
else {
    exit("You need php 5.3 or newer");
}
10
cen

実際には「3」がありますが、「u」と呼ばれています。

説明:bcryptには128ビットのソルトが必要です。指定するソルトは「変更されたbase64」である必要があります。つまり、文字、数字、「/」または「。」で構成されている必要があります。記号(true Base64 では「。」の代わりに「+」記号が使用され、順序が異なるため、これは「変更」されます)。これは64要素のアルファベットなので、各文字は6ビットの価値があり、22文字は132ビットをエンコードします。

Bcryptは132ビットの最初の128ビットのみを使用するため、最後の4ビットは完全に無視されます。最後の4ビットは、最後のソルト文字の最後の4ビットです。変更されたbase64では、「u」の値はバイナリで48、110000であり、「3」の値はバイナリで57、111001です。ご覧のとおり、2つの値は最後の(右端の)4ビットのみが異なり、無視されます。

何が起こるかというと、最初に使用するbcrypt実装は、ソルト(22文字)を128ビットバッファー(16バイト)に変換し、そこでドロップが発生します。次に、コードはそれをbackを変更されたbase64に変換しますが、今回は「欠落している4ビット」にゼロを使用するため、「3」が「u」に変わります。 「97504ebb48c4619f820f83」の代わりに「97504ebb48c4619f820f8u」をソルトとして使用すると、これらの2つのソルトは最後の4ビットのみが異なるため無視されるため、まったく同じbcrypt出力が得られます。

18
Thomas Pornin