web-dev-qa-db-ja.com

SRPまたはWebCryptoチャレンジ?

新しいWebサイト(HSTS + HPKPを使用したHTTPS)では、許可されたユーザーのデバイスでのみログインアクセスを制限したいと考えています。そのために、新しいデバイスごとにWebCrypto ECDSA公開/秘密鍵が生成されます。サーバーは新しいデバイスの公開鍵を保存し、デバイスIDを返します。ブラウザーは"device"という名前のIndexedDBに秘密キー(抽出不可)とデバイスIDを保存します。

ユーザーが自分のアカウントを登録するとき、または新しいデバイスを認証するとき、このデバイスのパスワードを尋ねますが、サーバーデータベースにパスワードを保存したくありません。

  • SRPプロトコルを使用してソルトと"ベリファイア"をサーバーに送信できますが、ユーザーパスワードではなく、派生パスワードを使用します(WebCrypto PBKDF2またはArgon2ライブラリ)。

  • または、WebCryptoを再度使用して、このデバイスのこのユーザーに固有の新しいECDSAキーを作成し、サーバーに公開キーを送信することもできます。ブラウザーでは、"base64(SHA-256(login)"という名前のIndexedDBにこのユーザーの秘密キーを保存しますが、AES-GCMアルゴリズムで暗号化(wrapKey関数)し、キーにはユーザーパスワードの導出(WebCrypto PBKDF2またはArgon2)を使用します次に、ログインするために、サーバーはチャレンジと単純なランダム文字列を送信し、クライアントはこの新しいユーザー固有の秘密鍵を使用してこのチャレンジの署名を返します(そのため、鍵をアンラップするには適切なパスワードを知っている必要があります)。

データベースが漏洩した場合:

  • SRPでは、検証者はユーザーのパスワードを簡単に推測することはできませんが、すべてのユーザーに新しいパスワードを尋ねる必要があると思います。

  • WebCryptoでは、それは公開鍵にすぎません。ユーザーはパスワードを変更する必要はありません。編集:欠点は、クライアント側で複数のパスワードチェックを実行できることです。たとえば、被害者がわかっている場合、サーバーに連絡する必要なく、JSコンソールで1000個のパスワードを使用してユーザーの秘密鍵をアンラップできます。 。

もちろん、ブラウザでIndexedDBが削除された場合、ユーザーは自分のアカウントを回復するためのプロセスを開始する必要があります(個人的な質問、電子メールによるOTPなど...主題ではありません)が、これはWebCryptoオプション。SRPオプションでも発生します。これは、デバイスIDを検出し、その署名をチェックして、ユーザーによって承認されたデバイスであることを確認する必要があるためです。

参考までに、この最初の認証チャレンジの後にU2Fも追加します。

私の特定のケースでは、SRPチャレンジを使用することをお勧めしますか、それともこのWebCryptoチャレンジを使用しますか?

4
lakano

私は私の質問に答えるためにこの解決策を提案します:

WebCrypto ECDSAキーは本当に便利ですが、私の質問で説明されているように(AES-GCMで暗号化されてラップされて)使用すると、JS Coderが被害者のブラウザーにアクセスし、JSコンソールで何千ものパスワードを試して、サーバーに接続せずに、適切なパスワードかどうかを確認します。したがって、サーバーとの対話なしにパスワードを試すことを許可しない方が確実に良いでしょう。私の解決策は、デバイスのECDSAキーのみを保持し、抽出できない秘密キーを使用し、暗号化せず、このデバイスのすべてのユーザーに対してグローバルにすることです。

SRPは広くテストされ、広く導入されていますが、DBリークが発生した場合、ブルートフォースからユーザーパスワードを取得するのが困難な場合でも、攻撃者がSRPソルトと検証者を持っているために可能です。したがって、最善の方法は、ユーザーパスワードではなくランダムなOTPを使用し、接続が成功するたびに更新することです。

まず、デバイスに秘密鍵が保存されていない場合:

  • 新しいWebCrypto ECDSAキーを生成する
  • このデバイスの公開鍵をサーバーに送信します
  • サーバーは、次のリクエストのためにdeviceUUIDと一種のJWTを返します

ユーザーが新しいデバイスにサインアップまたは自動化する場合:

  • 512ランダムビットのOTPを生成する
  • 派生OTPの128ランダムビットのソルトを生成します
  • Argon2 512ビットを使用してOTPを導出します。salt= derivedOTPSalt
  • SRPの128ランダムビットのソルトを生成します
  • この派生したOTPキーのSRPベリファイアを作成します
  • ユーザーがU2Fデバイスを持っている場合は、このデバイスに登録します
  • サーバーにdeviceUUID、派生したOTPキーのSRPソルト/ベリファイア、およびU2F PublicKeyを送信します。要求は、ECDSAデバイスの秘密鍵で署名されています。
  • ユーザー登録がサーバーによって受け入れられた場合、このデバイスのユーザーにパスワードを要求します
  • パスワード導出用に128ランダムビットのソルトを生成します
  • Argon2 512ビット、salt = derivedPasswordSaltを使用してユーザーパスワードを取得します。
  • マスクされたキーを作成します:OTP ^ derivedPassword(XOR)
  • ユーザーが作成したDB IndexedDB/sha256(login)に保存します:maskedKey、drivatedOTPSalt、derivedPasswordSalt、u2f =(true/false)

次に、ユーザーがサインインする場合:

  • ログインとdeviceUUIDをサーバーに送信し、SRP/U2Fチャレンジを要求します(サーバーへのすべての要求と同様に、ECDSA秘密鍵によって署名された要求)
  • デバイスの署名に問題がない場合、サーバーは次のメッセージを返します:challengeUUID、SRP Salt/Bチャレンジ、およびU2Fデバイスで署名するランダムテキストチャレンジ(ユーザーが持っていないことがわかっている場合でも)
  • ユーザーがU2Fデバイスを持っている場合は、チャレンジに署名するか、ランダムビットを生成します(任意のMITMをだますため)
  • このデバイスのユーザーパスワードを要求し、それを導出します(Argon2、salt = derivedPasswordSalt)
  • 格納されたmaskedKey ^ derivedPasswordAttempt(XOR)の結果を取得します
  • この結果(理論的には元のOTPを取得)から、Argon2/salt = derivedOTPSaltを使用してそれを導出し、これからA/M1 SRPチャレンジ回答を作成します
  • サインアップ方法に従って次のOTPも準備します
  • サーバーに送信:challengeUUID、U2Fチャレンジ、SRP A/M1チャレンジ、次のORPの次のSRPベリファイアとソルト。

サーバーは、それが適切なOTP、適切なECDSAデバイスシグネチャ、およびユーザーがU2Fデバイスを持っている場合は適切なU2Fシグネチャであることを確認できます。すべて問題なければ、サーバーは古いOTPベリファイア/ソルトを次のものに置き換え、新しい認証JWTを返します。

このソリューションでは、DBが漏洩した場合でも、実際のユーザーパスワードを取得することは不可能であり、派生OTPを取得することのみが可能であり、攻撃者がユーザーIDを偽装するだけでは不十分です。デバイス(および可能なU2Fデバイス)。

MITMの場合、ユーザーパスワードまたはOTPを読み取ることはできませんが、各OTP検証/塩を読み取ることはできます。それ以外の場合は、秘密のデバイスキーも必要であり、ユーザーのU2Fデバイスである可能性があるため、これでは不十分です。また、HTTPSとHSTS + HPKPを使用してMITM攻撃を防ぎます。

被害者のデバイスにアクセスできる攻撃者は、デバイスの秘密鍵を使用し、ソルトとマスクされたキーを読み取ることができますが、クライアント側でブルートフォース攻撃を行っても、パスワードを推測するのに役立ちません。 OTPがサーバーリクエストで問題ないかどうかを確認する必要があります。

このソリューションでは、認証が成功するたびに新しいOTPが発生するたびにリセットされます。 DBリークが発生した場合は、すべてのユーザーにプッシュ通知を送信して、OTPをリセットするためにサービスに接続するよう依頼するだけです。

したがって、このソリューションは、尊敬されるプロトコル(SRP、ECDSAデバイスキー、U2F検証、OTP、Argon2/PBKDF2)を使用し、セキュリティを強化するために混合されます。

あるピアがこの設計を見直して障害点を指摘できれば、これは本当にありがたいことです:)

1
lakano