web-dev-qa-db-ja.com

blowfishで長いパスワード(> 72文字)をハッシュする方法

先週、パスワードハッシュに関する多くの記事を読みましたが、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はbfishとも呼ばれるbcryptを使用してパスワードを保存し( https://shouldichangemypassword.com/Twitter-hacked.php )、推測:Twitterのパスワードを72文字以上の長いパスワードに変更し、最初の72文字のみを入力してアカウントにログインできます。

フグとコショウ

「ペッパーリング」パスワードについては、さまざまな意見があります。一部の人々は、それが不要だと言います。なぜなら、あなたは秘密の胡pepper文字列もまた、ハッシュを強化しないように知られている/公開されていると仮定しなければならないからです。私は別のデータベースサーバーを持っているので、データベースのみが漏えいし、コショウは絶えず流出する可能性が非常に高いです。

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

ですから、パスワードをペッパーにすることは、少なくとも悪い選択ではないと思います。

提案

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));
?>

これは this questionpassword_verify return false

質問

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

長所

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

短所

  • Blowfishハッシュの作成には64文字のみが使用されます


編集1:この質問はPHP blowfish/bcryptの統合。コメントをありがとう!

89
Frederik Kammer

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

文字ごとのエントロピー

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

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

ですから、私たちがどのように行動するかは、期待するキャラクターのタイプに依存します。

最初の問題

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

したがって、ペッパーをハッシュすることにより、パスワードで使用可能な最大エントロピーを2倍(576から288possibleビット)で効果的に削減できます。

第二の問題

ただし、_sha256_は最初に_256_ビットのエントロピーのみを提供します。したがって、可能性のある576ビットを256ビットに効果的に削減しています。あなたのハッシュステップ*すぐに*、まさに定義により少なくともpossibleエントロピーの50%パスワード。

_SHA512_に切り替えることでこれを部​​分的に解決できます。この場合、利用可能なエントロピーを約12%だけ削減します。しかし、それはまだ重要な違いではありません。その12%は、順列の数を_1.8e19_の係数で減らします。それは大きな数字です...そしてそれはfactorです.

根本的な問題

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

注:ここから先は、_SHA512_を生の出力(16進数ではない)で使用する胡systemシステムと比較していると想定しています。

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

    これらは、パスワードジェネレーターを使用して、パスワード用の大きなキーを生成するユーザーです。これらはランダム(生成され、人間が選択したものではない)で、文字ごとに高いエントロピーを持ちます。これらのタイプは、ハイバイト(127文字以上)といくつかの制御文字を使用しています。

    このグループでは、ハッシュ関数は大幅に利用可能なエントロピーをbcryptに削減します。

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

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

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

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

    もう一度言いましょう。このクラスのパスワードの場合、エントロピーがなくなる前に追加の6文字しか保存できません。

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

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

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

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

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

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

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

連続して72個の正しい文字をランダムに推測する可能性は、extremelyです。この衝突を起こすよりも、パワーボールの宝くじに21回勝つ可能性が高くなります。

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

実際に

実際には、それは実際には問題ではありません。誰かが最初の72文字を正しく推測する可能性は、後者の文字が大きな違いを生むので、心配する価値はありません。どうして?

さて、あなたはフレーズを取っているとしましょう。人が最初の72文字を正しく取得できる場合、それらはreallyラッキー(可能性は低い)であるか、一般的なフレーズです。一般的なフレーズの場合、唯一の変数は作成する時間です。

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

あなたは隣人の家を切望してはならない。あなたはあなたの隣人の妻、彼のしもべまたは女中、彼の牛またはろば、またはあなたの隣人に属するものを欲してはならない。

180文字です。 73番目の文字は、2番目の_neighbor's_のgです。あまり推測した場合は、おそらくneiで停止するのではなく、残りの節を続行します(パスワードが使用される可能性が高いため)。したがって、「ハッシュ」はあまり追加しませんでした。

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

結論

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

しかし、最終的には、どれもあまり重要ではありません。扱っている数字は、[〜#〜] way [〜#〜]が大きすぎます。エントロピーの違いはそれほど大きくありません。

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);
}
_

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

最も無知なアマチュアから最高の暗号作成者まで、誰でも彼自身が破ることのできないアルゴリズムを作成できます。

132
ircmaxell

パスワードをペッパーすることは確かに良いことですが、その理由を見てみましょう。

最初に、コショウが正確に役立つときに質問に答える必要があります。コショウは、パスワードが秘密のままである限りパスワードを保護するだけなので、攻撃者がサーバー自体にアクセスできる場合、それは役に立ちません。はるかに簡単な攻撃は、データベースへの読み取りアクセスを許可するSQLインジェクションです(ハッシュ値へ)。私は SQLインジェクションのデモ を用意して、それがどれだけ簡単かを示します(クリック準備された入力を取得するための次の矢印)。

それでは、ペッパーは実際に何を助けますか?コショウが秘密である限り、それは辞書攻撃から弱いパスワードを保護します。パスワード12341234-p*deDIUZeRweretWy+.Oのようなものになります。このパスワードは、はるかに長いだけでなく、特殊文字も含んでおり、辞書の一部にはなりません。

これで、ユーザーが使用するパスワードを推定できます。64〜72文字のパスワードを持つユーザーがいるため、おそらくより多くのユーザーが弱いパスワードを入力します(実際、これは非常にまれです)。

もう1つのポイントは、総当たり攻撃の範囲です。 sha256ハッシュ関数は、256ビットの出力または1.2E77の組み合わせを返します。これは、GPUの場合でも、ブルートフォースには大きすぎます(正しく計算された場合、2013年のGPUでは約 2E61年 が必要になります)。そのため、コショウを塗るのに本当の不利益はありません。ハッシュ値は体系的ではないため、一般的なパターンではブルートフォーシングを高速化できません。

追伸私が知る限り、72文字の制限はBCrypt自体のアルゴリズムに固有です。私が見つけた最良の答えは this です。

P.P.Sあなたの例には欠陥があると思います。完全なパスワード長でハッシュを生成することはできず、切り捨てられたもので検証することはできません。おそらく、ペッパーをハッシュの生成とハッシュの検証に同じ方法で適用するつもりでした。

5
martinstoeckli

Bcryptは、高価なBlowfishキーセットアップアルゴリズムに基づくアルゴリズムを使用します。

Bcryptの推奨される56バイトのパスワード制限(ヌル終了バイトを含む)は、Blowfishキーの448ビット制限に関連しています。その制限を超えるバイトは、結果のハッシュに完全には混合されません。したがって、bcryptパスワードの72バイトの絶対制限は、これらのバイトによる結果のハッシュへの実際の影響を考慮すると、あまり重要ではありません。

ユーザーが通常55バイトを超える長さのパスワードを選択すると思う場合は、代わりにパスワードストレッチのラウンドを常に増やして、パスワードテーブル違反の場合のセキュリティを強化できることに注意してください(ただし、これは、文字)。ユーザーのアクセス権が非常に重要で、通常ユーザーが非常に長いパスワードを必要とする場合、パスワードの有効期限も2週間のように短くする必要があります。これは、ハッカーが一致するハッシュを生成するかどうかを確認するために各トライアルパスワードのテストに関与する作業要素を無効にするためにリソースを投資している間、パスワードが有効のままになる可能性がはるかに低いことを意味します。

もちろん、パスワードテーブルが侵害されていない場合、ユーザーのアカウントをロックアウトする前に、ハッカーがユーザーの55バイトのパスワードを推測する試行は最大10回まで許可する必要があります;)

55バイトを超えるパスワードを事前ハッシュすることにした場合は、SHA-384を使用する必要があります。SHA-384は制限を超えずに最大の出力を得るためです。

2
Phil