95

先週、私はパスワードハッシュに関する多くの記事を読みました.Blowfish は現在最高のハッシュアルゴリズム (の 1 つ) のようですが、それはこの質問のトピックではありません!

72 文字の制限

Blowfish は、入力されたパスワードの最初の 72 文字のみを考慮します。

<?php
$password = "Wow. This is a super secret and super, super long password. Let's add some special ch4r4ct3rs a#d everything is fine :)";
$hash = password_hash($password, PASSWORD_BCRYPT);
var_dump($password);

$input = substr($password, 0, 72);
var_dump($input);

var_dump(password_verify($input, $hash));
?>

出力は次のとおりです。

string(119) "Wow. This is a super secret and super, super long password. Let's add some special ch4r4ct3rs a#d everything is fine :)"
string(72) "Wow. This is a super secret and super, super long password. Let's add so"
bool(true)

ご覧のとおり、最初の 72 文字だけが重要です。Twitter は bcrypt とも呼ばれるフグを使用してパスワードを保存しています ( https://shouldichangemypassword.com/twitter-hacked.php )。推測: Twitter のパスワードを 72 文字を超える長いパスワードに変更すると、次の方法でアカウントにログインできます。最初の 72 文字のみを入力します。

フグとペッパー

「ペッパー」パスワードについては、さまざまな意見があります。秘密のコショウ文字列も既知/公開されていると想定する必要があるため、ハッシュが強化されないため、不要であると言う人もいます。私は別のデータベースサーバーを持っているので、一定のコショウではなく、データベースのみがリークされる可能性は十分にあります。

この場合 (ペッパーが漏洩していない場合)、辞書に基づく攻撃をより困難にします (これが正しくない場合は訂正してください)。コショウのひもも漏れている場合:それほど悪くはありません-まだ塩があり、コショウのないハッシュと同じくらい保護されています.

したがって、パスワードをペッパーすることは、少なくとも悪い選択ではないと思います。

提案

72 文字 (およびコショウ) を超えるパスワードの Blowfish ハッシュを取得するための私の提案は次のとおりです。

<?php
$pepper = "foIwUVmkKGrGucNJMOkxkvcQ79iPNzP5OKlbIdGPCMTjJcDYnR";

// Generate Hash
$password = "Wow. This is a super secret and super, super long password. Let's add some special ch4r4ct3rs a#d everything is fine :)";
$password_peppered = hash_hmac('sha256', $password, $pepper);
$hash = password_hash($password_peppered, PASSWORD_BCRYPT);

// Check
$input = substr($password, 0, 72);
$input_peppered = hash_hmac('sha256', $input, $pepper);

var_dump(password_verify($input_peppered, $hash));
?>

これは次の質問に基づいています: password_verifyreturn false.

質問

より安全な方法は何ですか?最初に SHA-256 ハッシュ (64 文字を返す) を取得するか、パスワードの最初の 72 文字のみを考慮しますか?

長所

  • ユーザーは最初の 72 文字だけを入力してもログインできません
  • 文字数制限を超えずにコショウを追加できます
  • hash_hmac の出力には、おそらくパスワード自体よりも多くのエントロピーがあります
  • パスワードは 2 つの異なる関数によってハッシュされます

短所

  • フグのハッシュを作成するために使用されるのは 64 文字のみです。


編集 1:この質問は、フグ/bcrypt の PHP 統合のみに対応しています。コメントありがとうございます!

4

3 に答える 3

144

ここでの問題は基本的にエントロピーの問題です。それでは、そこから見てみましょう。

文字ごとのエントロピー

バイトあたりのエントロピーのビット数は次のとおりです。

  • 16 進文字
    • ビット: 4
    • 値: 16
    • 72 文字のエントロピー: 288 ビット
  • 英数字
    • ビット: 6
    • 値: 62
    • 72 文字のエントロピー: 432 ビット
  • 「共通」記号
    • ビット: 6.5
    • 値: 94
    • 72 文字のエントロピー: 468 ビット
  • フルバイト
    • ビット: 8
    • 値: 255
    • 72 文字のエントロピー: 576 ビット

したがって、私たちがどのように行動するかは、期待するキャラクターのタイプによって異なります。

最初の問題

コードの最初の問題は、「ペッパー」ハッシュ ステップが 16 進文字を出力していることです (4 番目のパラメーターhash_hmac()が設定されていないため)。

したがって、コショウをハッシュすることで、パスワードに使用できる最大エントロピーを 2 分の 1 (576 ビットから 288 ビットまで) に効率的に削減できます。

第二の問題

ただし、そもそもエントロピーのビットsha256しか提供しません。256つまり、可能な 576 ビットを効果的に 256 ビットに削減しています。あなたのハッシュステップは*すぐに*、まさに定義上 、パスワードで可能なエントロピーの少なくとも50%を失います.

に切り替えることSHA512で、これを部分的に解決できます。使用可能なエントロピーは約 12% しか減少しません。しかし、それはまだ重要な違いではありません。その 12% により、順列の数が 1/1 に減少し1.8e19ます。それは大きな数字です...そして、それがそれを減らす要因です...

根本的な問題

根本的な問題は、72 文字を超える 3 種類のパスワードがあることです。このスタイル システムが彼らに与える影響は大きく異なります。

SHA512注:ここから先は、生の出力(16進数ではない)を使用するペッパーシステムと比較していると仮定しています。

  • 高エントロピーのランダム パスワード

    これらは、パスワード用に大量のキーを生成するパスワードジェネレーターを使用しているユーザーです。それらはランダム (生成されたものであり、人間が選択したものではありません) であり、1 文字あたりのエントロピーが高くなります。これらのタイプは、上位バイト (文字 > 127) と一部の制御文字を使用しています。

    このグループの場合、ハッシュ関数により、利用可能なエントロピーが に大幅にbcrypt削減されます。

    もう一度言わせてください。高エントロピーで長いパスワードを使用しているユーザーの場合、このソリューションはパスワードの強度を測定可能な量だけ大幅に低下させます。(72 文字のパスワードでは 62 ビットのエントロピーが失われ、より長いパスワードではさらに多くのエントロピーが失われます)

  • 中程度のエントロピーのランダム パスワード

    このグループは、一般的な記号を含むパスワードを使用していますが、上位バイトや制御文字は使用していません。これらは入力可能なパスワードです。

    このグループでは、より多くのエントロピーをわずかにロック解除します (作成するのではなく、より多くのエントロピーが bcrypt パスワードに収まるようにします)。私がわずかに言うとき、私はわずかに意味します。SHA512 が持つ 512 ビットを最大限に活用すると、損益分岐点が発生します。したがって、ピークは 78 文字です。

    もう一度言わせてください。このクラスのパスワードでは、エントロピーを使い果たす前に追加で 6 文字しか保存できません。

  • 低エントロピーのランダムでないパスワード

    これは、おそらくランダムに生成されていない英数字を使用しているグループです。聖書の引用などのようなもの。これらのフレーズには、1 文字あたり約 2.3 ビットのエントロピーがあります。

    このグループの場合、ハッシュ化することで、より多くのエントロピーを大幅にロック解除できます (エントロピーを作成するのではなく、bcrypt パスワード入力にさらに多くを収めることができます)。損益分岐点は、エントロピーを使い果たす前に約 223 文字です。

    もう一度言いましょう。このクラスのパスワードの場合、事前ハッシュによって確実にセキュリティが大幅に向上します。

バック・トゥ・ザ・リアル・ワールド

この種のエントロピー計算は、現実の世界ではあまり重要ではありません。重要なのは、エントロピーを推測することです。それが、攻撃者ができることに直接影響を与えるものです。それがあなたが最大化したいことです。

エントロピーの推測に関する研究はほとんどありませんが、指摘したい点がいくつかあります。

連続して 72 文字の正しい文字をランダムに推測できる可能性は非常に低いです。パワーボールの宝くじで 21 回当選する可能性は、この衝突が発生する可能性よりも高くなります... これが、私たちが話している数字の大きさです。

しかし、統計的にはつまずかないかもしれません。フレーズの場合、最初の 72 文字が同じである可能性は、ランダムなパスワードの場合よりもはるかに高くなります。しかし、それでもかなり低いです (1 文字あたり 2.3 ビットに基づくと、パワーボールの宝くじに 5 回当選する可能性が高くなります)。

特に

実際には、それは本当に問題ではありません。誰かが最初の 72 文字を正しく推測し、後者が大きな違いを生む可能性は非常に低いため、心配する必要はありません。なんで?

さて、あなたがフレーズを取っているとしましょう。その人が最初の 72 文字を正しく理解できた場合、その人は本当に幸運である (可能性は低い) か、それが一般的なフレーズであるかのどちらかです。それが一般的なフレーズである場合、唯一の変数はそれを作成する長さです.

例を見てみましょう。聖書から引用してみましょう (聖書が長いテキストの一般的なソースであるという理由だけで、他の理由ではありません)。

隣人の家をむさぼってはならない。あなたの隣人の妻、または彼のしもべまたは女中、彼の牛またはろば、またはあなたの隣人のものを欲しがってはならない.

180文字です。73 番目の文字はg2 番目のneighbor'sです。そこまで推測できた場合は、 で停止するのでneiはなく、残りの節に進んでいる可能性があります (これがパスワードの使用方法である可能性が高いためです)。したがって、「ハッシュ」はあまり追加されませんでした。

ところで: 私は絶対に聖書の引用を使用することを主張していません. 実際、正反対です。

結論

最初にハッシュすることによって長いパスワードを使用する人々を実際に助けるつもりはありません。あなたが間違いなく助けることができるいくつかのグループ。あなたが間違いなく傷つけることができるものもあります。

しかし、結局のところ、どれも過度に重要ではありません。私たちが扱っている数字は、あまりにも高すぎます。エントロピーの差はそれほど大きくありません。

bcrypt はそのままにしておく方がよいでしょう。防止しようとしている攻撃が発生する可能性よりも、ハッシングを台無しにする可能性が高くなります (文字通り、すでにそれを行っており、その間違いを犯したのはあなたが最初でも最後でもありません)。

サイトの残りの部分を保護することに集中してください。また、登録時にパスワードボックスにパスワードエントロピーメーターを追加して、パスワードの強度を示します(パスワードが長すぎてユーザーが変更したい場合があることを示します)...

それは私の少なくとも0.02ドルです(またはおそらく0.02ドル以上)...

「秘密の」コショウを使用する限り:

文字通り、1 つのハッシュ関数を bcrypt にフィードする研究はありません。したがって、「ペッパー」ハッシュを bcrypt にフィードすることで未知の脆弱性が発生するかどうかは、せいぜい不明です (そうhash1(hash2($value))することで、衝突耐性とプリイメージ攻撃に関する重大な脆弱性が明らかになる可能性があることがわかっています)。

秘密鍵 (「コショウ」) を保存することを既に検討していることを考えると、十分に研究され理解されている方法で秘密鍵を使用してみませんか? ハッシュを保存する前に暗号化しないのはなぜですか?

基本的に、パスワードをハッシュした後、ハッシュ出力全体を強力な暗号化アルゴリズムにフィードします。次に、暗号化された結果を保存します。

現在、SQL インジェクション攻撃は、暗号鍵を持っていないため、有用なものを漏らすことはありません。また、キーが漏洩した場合、攻撃者は、プレーン ハッシュを使用した場合よりも有利になることはありません (これは証明可能であり、ペッパーの「事前ハッシュ」を使用したものでは提供されません)。

注: これを行う場合は、ライブラリを使用してください。PHP については、Zend Framework 2 のパッケージを強くお勧めします。Zend\Crypt実際、現時点で私がお勧めするのはこれだけです。十分に検討されており、ユーザーに代わってすべての決定を下します (これは非常に良いことです)...

何かのようなもの:

use Zend\Crypt\BlockCipher;

public function createHash($password) {
    $hash = password_hash($password, PASSWORD_BCRYPT, ["cost"=>$this->cost]);

    $blockCipher = BlockCipher::factory('mcrypt', array('algo' => 'aes'));
    $blockCipher->setKey($this->key);
    return $blockCipher->encrypt($hash);
}

public function verifyHash($password, $hash) {
    $blockCipher = BlockCipher::factory('mcrypt', array('algo' => 'aes'));
    $blockCipher->setKey($this->key);
    $hash = $blockCipher->decrypt($hash);

    return password_verify($password, $hash);
}

そして、(少なくとも比較的) 十分に理解され、十分に研究された方法ですべてのアルゴリズムを使用しているため、有益です。覚えて:

最も無知なアマチュアから最高の暗号学者まで、誰でも、自分では解読できないアルゴリズムを作成できます。

于 2013-05-16T21:00:24.267 に答える