web-dev-qa-db-ja.com

PBKDF2には標準化された実装がないようです?

私は最近、DjangoについてのバグについてPBKDF2に関して大きなパスワードでサービス拒否を引き起こしていることについて読みました:

https://www.djangoproject.com/weblog/2013/sep/15/security/

これは、PBKDF2が各反復で入力パスワードを「混合」するため、大量の入力の影響が増幅されるためです。 Djangoもともとは、4096バイトのパスワード文字制限を設定することでこれを解決しました。

私はGoでこれを再現しようとしましたが、それらの実装に脆弱性がないことがわかりました。これは、反復ループを実行する前に入力パスワードをHMACするため、反復は常に一定サイズの入力に対して行われるためです。問題のソースは次のとおりです。

https://github.com/golang/crypto/blob/master/pbkdf2/pbkdf2.go

次に、最大入力サイズを設定するDjango "fix"が逆になっていることがわかりました。代わりに、Goと同様のソリューションを実装することで修正しました。

https://github.com/Django/django/commit/68540fe4df44492571bc610a0a043d3d02b3d32

これは私にとって不可解です。 PBKDF2仕様では、最初にHMACを実行する必要があると記載されていますか?私の知る限りでは、そうではありません。これは、Goと現在のDjango実装が仕様に準拠していないことを意味しますか?以前のDjango実装はより適切でしたが、PBKDF2を変更しましたDoS問題を修正するには?

OpenBSD実装は、初期HMACも実行しないようです: http://bxr.su/OpenBSD/lib/libutil/pkcs5_pbkdf2.c

ただし、PHP実装では次のようになります: https://github.com/php/php-src/blob/1c295d4a9ac78fcc2f77d6695987598bb7abcb83/ext/hash/hash.c#L600-L72 =

この点で標準化されていないように見えるのはなぜですか? 「正しい」実装はどれですか?

PS:PBKDF2はではなく最初にHMAC化することを意図したものではなく、「ユーザーフレンドリーな」ラッパーであるという結論に達した私はそれについてこの議論を見つけましたPBKDF2アルゴに渡す前にHMACを実行する必要があります: https://github.com/defuse/php-encryption/issues/2

それは、RFCの私の読書に基づいて私には理にかなっています。あれは正しいですか?

4
Ryan Sheasby

Golangの実装が、反復ループを実行する前に入力パスワードのHMACを計算し、PBKDF2を非標準に準拠させるとすると、完全に正確ではありません。

PBKDF2のGolang実装は、完全に標準に準拠し、相互運用可能です。

最初に注意してください-厳密に言えば-PBKDF2にはPRF(Pseudo Random Function)が必要です。 Golang暗号ライブラリのPBKDF2は、PRFがHMACとして実装されている場合にのみ機能するという点で制限されています(実際には誰もがそうするので、これは問題ありません)。

そうは言っても、PRFがHMACであると仮定すると、PBKDF2の内部で最も時間のかかるループがこれを実行します。

_U_1 = HMAC(password, S || INT (i)) ,
U_2 = HMAC(password, U_1) ,
...
U_c = HMAC(password, U_{c-1}) .
_

ここで、cは非常に大きな数であり、すべての_U_x_項目は短く、長さが基本的なハッシュ関数の出力と一致します(SHA-256の32バイトなど)。

非常に長いパスワードの場合、HMACは実際には次のような構成です。

_Kp = SHA-256(password)
HMAC(password, data) = SHA-256(Kp xor constant1 || SHA-256(Kp  xor constant2 || data))
_

単純な実装では、PBKDF2ループの反復ごとにKp = SHA-256(password)の値がまったく同じ(つまり、c回)を計算できることに気づくかもしれません。 (Djangoで発生したように)攻撃者が任意のパスワードを自由に選択できる場合、DoSにつながる可能性があります。

ほとんどの実装(Golangなど)はより注意深く、SHA-256関数の中間計算をdataまでキャッシュします。これは、反復ごとに変化する唯一の値です。つまり、

_HMAC(password, data) = SHA-256(Kp xor constant1 || SHA-256(Kp  xor constant2 || data))
                       ^^^^^^^^^^^^^^^^^^^^^^^^    ^^^^^^^^^^^^^^^^^^^^^^^^^
                                         CACHED ACROSS ITERATIONS
_

そのような最適化された実装では、すべての反復は、小さなdataコンポーネントに供給することにより、キャッシュされた値から始めてHMACを完了することで構成されます。アルゴリズムのコストはほぼ固定され、パスワードの長さとは無関係です(キャッシュされた状態を作成するために必要なステップは別として)。

結論: PBKDF2のGolang実装は完全に標準に準拠し、相互運用可能ですが、PRFがHMACである場合にのみ機能します。実装はDoS攻撃に対して安全です。これは、シークレットパスワードからの寄与を含め、すべての反復で常に同じである中間HMAC状態を事前計算するためです。ただし、これはパスワードのHMACではなく、標準アルゴリズムの最適化にすぎません。