web-dev-qa-db-ja.com

ユーザーパスワードから2つのキーを取得-1つはログイン用、もう1つは暗号化用?

コンテキスト

ブラウザベースのクライアントサーバーアプリケーションがあります。クライアントは電子メールとパスワードで登録します。パスワードはいくつかのガイドラインに準拠しています。

問題

ユーザーは、クライアントデバイスのユーザーのみが一時的に使用するためにキーを復号化および回復できるように暗号化キーを格納するために、サーバー上に暗号化された格納域を必要とします。
そのため、サーバーや他の誰かがそれらの暗号化キーにアクセスすることはできません。クライアント側のユーザーのみが回復できます。

ソリューション??

  1. クライアント:ユーザーは電子メールとパスワードで登録します
  2. クライアント:bcrypt(password、salt)を使用してマスターシークレット(MS)を取得する
  3. クライアント:MSからHKDF(MS、salt)を使用してログインシークレット(LS)を取得
  4. クライアント:MSからHKDF(MS、salt)を使用してVault-Secret(VS)を派生
  5. クライアント:電子メール+ LSをサーバーに送信します。
  6. サーバー:bcrypt(LS、random_salt)によってLSをDBに格納します(他のパスワードと同様)。
  7. クライアント:VSを使用して暗号鍵を復号化し、サーバーに送信できます。

手順2〜4で使用される3つのソルトは、SHA3-512を使用して電子メールから取得でき、これらの機能に必要なバイト数を使用できますか?

サーバーにボールトを復号化する方法に関する情報がないことが重要だと考えていました。したがって、ボールトの秘密鍵はいつの時点でも知るべきではありません。ログインと暗号化に1つのパスワードを使用するだけでは合理的ではないと思いますか?誰かがこの問題にどのように取り組みますか?

1
Dachstein

私が提案した重複 は「そうです、これは合理的な方法で進んでいます」と言うべきだと思いますが、個別に扱う価値があると思う点がいくつかあります。

手順2〜4で使用される3つのソルトは、SHA3-512を使用して電子メールから取得でき、これらの機能に必要なバイト数を使用できますか?

塩は再利用してはならず、また予測可能であってはなりません。ユーザー名のハッシュを使用することにより、ユーザーがパスワードを変更するたびに同じソルトが使用されます。また、攻撃者は、使用されるソルトを簡単に予測し、将来のリークによってそれらが使用されることを期待してレインボーテーブルの生成を開始することもできます。

ログインキーソルトの場合、ログインする前にクライアント側で使用する必要があるため、ユーザー名を知っている人はとにかくサーバーからソルトを取得できる必要があるため、ユーザー名からソルトを生成することはできません。ここでは取引ブレーカーですが、ユーザーがパスワードを変更するときは、同じソルトを使用しないことをお勧めします。レインボーテーブルの事前計算を防ぐために、暗号化キーソルトはランダムに生成する必要があります。つまり、サーバーに保存する必要があります(ユーザーがブラウザーやコンピューターを変更できないことに問題がない場合)。

これを、サーバーにパスワードを送信するだけの通常のアプリと比較すると、実際には外部の攻撃者にとって安全性は低くありません。ログインキーソルトを使用するレインボーテーブルが役立つためには、誰かがユーザーのログインキーを取得する必要があります。これは他のアプリではパスワード自体になります。

暗号化キーソルトが認証されたユーザーにのみ送信される場合、生成できる唯一のRainbowテーブルはユーザーのログインキー用です。データベースリークがある場合、ログインキーのサーバー側のbcryptハッシュには、レインボーテーブルでは役に立たない個別のランダムソルトが含まれ、暗号化されたデータは、攻撃者が使用した暗号化キーソルトを使用して暗号化されます。には事前のアクセス権がないため、誰でもログインキーsaltにアクセスできるようにしても、それほど脅威になることはありません。

もちろん、サーバー自体が悪意のあるものである場合、itがパスワードを解読しようとするのを妨げるものは何もありませんが、これは実際には回避できません。

サーバーにボールトを復号化する方法に関する情報がないことが重要だと考えていました。

これは確かにいいように思えますが、ユーザーが極端な対策(つまり、コードを監査し、ページを読み込むたびにコードが変更されていないことを確認する)を行わない限り、ある程度サーバーを信頼する必要があります。ページをロードするたびにJavaScriptを送信するのはサーバーであり、サーバーを制御する悪意のあるユーザーがコードを変更して暗号化キーを送信するのは簡単です。

ユーザーのみが知っているキーを使用したクライアント側の暗号化は、将来サーバーが危険にさらされた場合の被害を軽減するのに役立ちますが、ユーザーが危険にさらされている/悪意のあるユーザーがサーバーに接続した場合、サーバーはおそらく使用できなくなります幸運。

したがって、ボールトの秘密鍵はいつの時点でも知るべきではありません。ログインと暗号化に1つのパスワードを使用するだけでは合理的ではないと思いますか?

この問題を解決するには、暗号化キーと認証に個別のソルトを使用してbcryptを介してパスワードを実行し、次にHKDFクライアント側で十分です。歴史的に、JavaScriptはその速度のためにパスワードハッシュに適した選択肢ではなかったことに注意してください。ネイティブ実装がJavaScript実装よりも10倍速い場合、JavaScriptは同じセキュリティで10倍長く実行する必要があります。 WebAssemblyの実装はより適切に機能する可能性がありますが、ベンチマークをお勧めします。

2
AndrolGenhald

これはまさに、私が1Passwordを設計するときに直面した問題です(私が作業している)。細かい詳細が必要な場合は、 1Password Security Design document を参照してください。 (そのKDFを今日設計する場合に異なる方法で行うこともあります。インスピレーションを得るために使用してください。ただし、完全にコピーしないでください。)

詳細の多くは異なりますが、全体的なアプローチは似ています。 「マスターロック解除キー」の導出には、長期的なクライアント認証シークレットの導出に使用されるものとは異なる、別個のソルトと別個のHDKF情報を使用します。

認証シークレットを送信しないでください

あなたと同じように、私たちは決してユーザーの秘密を受け取りたくないことに注意してください。つまり、LSと呼んでいるものが送信されることはありません。代わりに、パスワード認証キー交換メカニズム、特にSRPを使用します。それだけの価値があるので、オープンソース SRP実装 をオープンしました。ソルトをサーバーから利用できるようにしますが、将来のバージョンではソルトを盲検化して、一部の事前計算攻撃を防ぐことができます。

遅いハッシュ

Bcryptの選択は良い選択です。 Argon2やscryptなどのメモリハードKDFを検討することもできます。私たちの場合、Webブラウザーで十分に最適化された実装が必要なものが必要だったため、PBKDF2を使用できませんでした。ただし、Webクライアントを使用しているのではない場合は、少なくともbcryptまたはメモリがハードなKDFの1つに注目してください。

PBKDF2に関するもう1つの注意事項は、キーstretchingに使用しないことです。キーストレッチングにHKDFを正しく使用していますが、これを読んでいる他の人にその警告を入れたかったのです。 PBKDF2にはデザインエラーがあり、状況によっては、キーストレッチに使用すると、後部に食い込む可能性があります。

低速ハッシュの限界と真の解読不能性

Bcryptなどが必要ですが、これらすべての 遅いハッシュは戻り値が減少します になる点があります。そして人々はbasパスワードを使用します。そこで、クライアントにのみ保存される高いエントロピーシークレットをキー導出関数に追加します。これは、マスターパスワードクライアント側と組み合わされます。クライアントが保持する秘密がないと、私たちが保持するデータはパスワードの解読に使用できません。

その他

ユーザー名から派生するのではなく、ランダムな塩を使用することをお勧めします。これは、サービスがユーザーに新しいクライアントをセットアップするときにソルトを送信する必要があることを意味しますが、ログインシークレットを送信しないように、クライアントとサーバー間でチャレンジ/レスポンスを使用する必要があります。

ユーザー名からソルトを派生させたい場合、SHA3-512はソルト作成には過剰です。それほど長くするために塩は必要ありません。ソルトの目的は、同じパスワードを持つキーの衝突を回避することです。ソルトには16バイトで十分です。

2