web-dev-qa-db-ja.com

BCryptまたはPBKDF2がすでに計算されていて、元のパスワードがない場合、BCryptまたはPBKDF2のコストを増やすことはできますか?

これら2つのアルゴリズムのコスト(反復)をオフラインで増やすことができるかどうかを知りたかっただけです。ユーザーのパスワードのコストを毎年増やしたい。

1つの解決策は、ユーザーがログインしたときにそれらを再計算することですが、ユーザーはその期間にログインしていない可能性があり、ログインするまで待ちたくありません。

これはパスワードストレッチ(たとえば、sha-256ハッシュの反復)で実行できますが、BCryptやPBKDF2でこれが可能かどうかはわかりません。ありがとうございました。

31
skantos

PBKDF2とBcryptは、パスワードを知らずに、指定された反復回数での出力から始めて、コストの増加をサポートしていません。そのための本質的な理由はありません。パスワードハッシュプロセスcouldこのようなオフラインストレッチを可能にしながら、「良好」である。しかし、これらのアルゴリズムはそれを許さないのです。

実行できることは次のとおりです。通常のbcryptまたはPBKDF2出力には、salt s、反復回数i、およびハッシュ出力vが含まれます。 bcrypt実装では、3つの値が(Base64のようなエンコーディングで)印刷可能な文字にエンコードされることがよくあります。たとえば この答え を参照してください。 svがあるとすると、次のものを格納できます。

  • s;
  • 反復回数i;
  • 追加の反復回数j;
  • h(h(h(... h(h(v))...)))]これはハッシュvを繰り返した結果であり、安全なハッシュ関数h、完了j回。

パスワードを検証するには、指定したパスワードからbcrypt/PBKDF2を再計算し(sおよびiを使用)、結果の値をハッシュする必要がありますj =回、格納された値と一致するかどうかを確認します。

これはほとんど安全です。ifhには、SHA-256のような強力なハッシュ関数を使用します。繰り返されたハッシュは可能な値のスペースを減らしますが、ハッシュ出力値がサイズのスペースにある場合、サイズの内部「サイクル」にほぼ到達するはずですsqrt(N)- [〜#〜] n [〜#〜];さらに、そのサイクルが徹底的に調査できるほど小さい場合は、ハッシュ関数の衝突hを計算できます(ニース図については このページ を参照)。したがって、hが(SHA-256のように)衝突耐性がある場合、上記のスキームは安全です。

重要な点は、オフラインでjを後で増やすことができるということです。 ただし、この種のストレッチは、追加のハッシュ関数の計算に伴う計算コストに依存することに注意してください。残念ながら、SHA-256のような通常のハッシュ関数は、GPUが実行できる機能に非常によく対応しています。したがって、攻撃者に対してそのようにして得られる利点は、最初に望んだほど大きくはないかもしれません。言い換えると、上記のストレッチを使用すると、PBKDF2よりもbcryptの利点が失われます(この問題についての説明は、 この答え を参照してください)。上記のユーザーが戻ってくるまで、一時的な手段としてそれを適用して、より高い反復回数(i)でbcryptステップを再度実行できるようにすることができます。

さらに、、上記で説明したスキームは自家製の暗号であり、それは悪いことです。私は完全に素晴らしいので、それでうまくいくことができますが、これは一般的に宣伝することはできません。

14
Thomas Pornin

bcryptは、次のように定義されるEksblowfish alghoritmに依存します。

_Eksblowfish(cost, salt, key)
  state = InitState()
  state = ExpandKey(state, salt, key)
  repeat (2^cost)
    state = ExpandKey(state, 0, key)
    state = ExpandKey(state, 0, salt)
  return state
_

このコードは反復回数を示しています。 bcryptの使用法はbcrypt(cost, salt, key)なので、cost変数は反復のlog2です。したがって、costに1を追加すると、時間が2倍になります。

各ラウンドは初期パスワード(キー)を使用するため、それなしで追加のラウンドを追加することはできません。したがって、Bcryptの場合、答えはノーです。

9
p____h

P____hが言及しているように、Eksblowfishアルゴリズムはすべてのラウンドでキー(およびソルト)を使用し、最終状態のみを更新し、最終的に返します。*したがって、元のキーがなければ、ラウンド数を増やすことはできません。

PBKDF2フレームワークは、その内部ループを同様に定義します。

F(P,S,c,i) = U1 ^ U2 ^ ... ^ Uc
U1 = PRF(P,S || INT_msb(i))
U2 = PRF(P,U1)
...
Uc = PRF(P,Uc-1)

そして同じ問題に直面しています。

考えられる代替策の1つは、データベースに古いハッシュがある場合、それを新しい強力なハッシュのキーとして使用し、それを破棄することです。古いソルトとワークファクターの値を記録する必要があります(これらは通常、最終出力に記録されます)。ログインしたユーザーが古いソルトと古い作業値を取得したら、中間ハッシュを計算し、それをセカンダリハッシュのキーとして使用します(セカンダリソルトと作業係数が格納されます)。パスワードがチェックアウトされている場合は、パスフレーズから直接新しい強力なハッシュを計算し、ユーザーを更新済みとしてマークできます。

N.B.暗号化は直感に反する場合があります。私は、そのような連鎖の結果として生じる困難さの増加(もしあれば)を分析していません。


InitStateを呼び出して、新しいキースケジュールにの数字を入力した後、EksBlowfishSetupはソルトとキーを使用してExpandKeyを呼び出します。これにより、後続のすべての状態が両方に依存し、ソルトとキーの両方がないとアルゴリズムのどの部分も事前計算できないことが保証されます。その後、ExpandKeyは繰り返しでソルトとキーを使用して交互に呼び出されます。 ExpandKeyの最初の呼び出し以外のすべての場合、2番目の引数は128 0ビットのブロックです。これは、元のblowfishのキースケジュールにより似ています。また、レジスターが少ないCPUアーキテクチャーでEksBlowfishSetupをより効率的に実装できます。

7

私は同じ要件を持っていて、この方法でそれを解決しました:

  • PBKDF2と現在の作業係数が16である通常の方法でソルトとパスワードをハッシュします(つまり、2 ^ 16反復)。ソルト、ハッシュ、作業係数などをデータベーステーブルの行として保存します。
  • 非同期的に(つまり、エンドユーザーに影響を与えずに)これをさらに3回行い、作業係数を毎回1ずつ増やします。したがって、このユーザーには4つのデータベース行があり、作業係数はそれぞれ16、17、18、19です。
  • 私は常にパスワードの検証に最も低い作業係数の行を使用しますが、2年ごとに最も低い作業係数の行を削除します。
5
HTTP 410

元のパスワードなしで鍵のラウンド数を長くすることが可能だったと想像してください。その場合、1回の反復後の出力から1000回の反復後の出力にマップするRainbowテーブルを作成することができます。このテーブルを使用すると、1000回の反復を簡単に元に戻すことができます。

そのテーブルが与えられた場合、クラッカーは1000回の反復を1に戻し、次に1回の反復だけでPBKDFのクラックを試みることができます。それはアルゴリズムを弱めます。

0
Eyal