ここでの問題は基本的にエントロピーの問題です。そこで、そこから見ていきましょう。
文字あたりのエントロピー
バイトあたりのエントロピーのビット数は次のとおりです。
- 16進文字
- ビット:4
- 値:16
- 72文字のエントロピー:288ビット
- 英数字
- ビット:6
- 値:62
- 72文字のエントロピー:432ビット
- 「共通」の記号
- ビット:6.5
- 値:94
- 72文字のエントロピー:468ビット
- 全バイト
- ビット:8
- 値:255
- 72文字のエントロピー:576ビット
したがって、どのように行動するかは、予想される文字のタイプによって異なります。
最初の問題
コードの最初の問題は、「ペッパー」ハッシュステップが16進文字を出力することです(4番目のパラメーターhash_hmac()
が設定されていないため)。
したがって、コショウをハッシュすることで、パスワードに使用できる最大エントロピーを2倍(576から288 可能なビット)に効果的に削減できます。
第二の問題
ただし、そもそもエントロピーのビットをsha256
提供するだけ256
です。つまり、可能な576ビットを256ビットに効果的に削減しています。ハッシュステップは、*すぐに*、定義により、パスワードの可能なエントロピーの少なくとも 50%を失い
ます。
これを部分的に解決するには、に切り替えSHA512
ます。使用可能なエントロピーは約12%しか減少しません。しかし、それはまだ重要な違いではありません。その12%は、順列の数をの係数で減らし1.8e19
ます。それは大きな数字です...そしてそれがそれを減らす要因です...
根本的な問題
根本的な問題は、72文字を超える3種類のパスワードがあることです。このスタイルシステムがそれらに与える影響は大きく異なります。
注:ここからはSHA512
、生の出力(16進数ではない)を使用するペッパーシステムと比較していると仮定します。
高エントロピーランダムパスワード
これらのユーザーは、パスワードジェネレーターを使用して、パスワード用の大きなキーを生成します。それらはランダムで(人間が選択したものではなく)生成され、キャラクターごとに高いエントロピーを持っています。これらのタイプは、ハイバイト(文字> 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番目の文字はg
2番目のneighbor's
です。それほど多くを推測した場合は、おそらくで停止するので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);
}
また、すべてのアルゴリズムをよく理解され、十分に研究された方法で使用しているため、これは有益です(少なくとも比較的)。覚えておいてください:
最も無知なアマチュアから最高の暗号学者まで、誰でも彼自身が破ることができないアルゴリズムを作成できます。