web-dev-qa-db-ja.com

ユーザー識別および認証トークンとしてのJSON Web Token(JWT)

認証を必要とするRESTサービスを実装しています。このサービスはデータベースに直接アクセスできないため、ランダムに生成されたトークンなどのユーザーごとの状態を保存できません。別のバックエンドサービスへ。

私が思いついた解決策は、ユーザーが認証するときにJSON Webトークン( [〜#〜] jwt [〜#〜] )を作成することです。 JWTクレームセットには、Subject( "sub")フィールドにユーザーIDが含まれています。次に、サーバーは、256ビットキーのAES GCM( "enc": "A256GCM")を使用してクレームセット( "alg": "dir")を直接暗号化し、 [〜#〜] jwe [〜#〜]を作成します。 。キーは、サービスの開始時に一度生成され、メモリに保存されます。

認証するために、クライアントはユーザー名/パスワードを送信し、サーバーは上記のトークンで応答します。次に、クライアントは後続の各要求でそのトークンを送信します。

クライアントが後続のリクエストでトークンを送信すると、サーバーはキーを使用してトークンを復号化し、「sub」フィールドのユーザーIDを現在のユーザーのIDであると想定します。認証チェック。トークンの有効期限は、JWTクレームセットの「exp」フィールドによって処理されます。

クライアントとサーバー間の接続はSSL/TLSを使用するため、トークンは漏洩しません。

私は this library を使用してJWTの作成と読み取りを行っています。自分が正しい暗号化コードを書くことを信頼していないからです。

私の質問:

  1. 上記のアプローチは安全ですか?攻撃者はトークンを操作して別のユーザーになりすますことはできますか?
  2. アプローチは過度に複雑ですか?暗号化の代わりにMAC(言い換えれば [〜#〜] jws [〜#〜] )を使用しても同じセキュリティがありますか? (それはより簡単で、間違いをする可能性が少ないので、おそらくそれ以上です)。 JWTクレームセットには特に秘密はありません。ユーザーは自分のIDを知っていてもかまいません。
  3. JWEアルゴリズムと暗号化の私の選択は適切ですか?
    • JWE「alg」の場合、私が使用しているライブラリは、直接暗号化(キーを使用してクレームセットを直接暗号化)とRSA(新しいキーを生成して各トークンのクレームセットを暗号化し、生成されたキーをRSA公開鍵)。 RSAキーよりも対称キーを生成する方が簡単なので、前者を選択しました。
    • JWE「enc」の場合、ライブラリはAES GCMおよびAES CBC HMAC SHA2(さまざまなビット長)をサポートします。私は任意にGCMを選択しました。
70
imgx64

基本的なアプローチは有効です。ユーザーがログインしたときにJWTを生成し、後続のメッセージにJWTが含まれることを期待し、JWTが有効であれば、後続のメッセージでJWTの件名フィールドを信頼します。ただし、注意すべき点がいくつかあります。

  1. 大雪が言うように、MAC( "alg": "HS256")は、ペイロードの変更を防ぐように特別に設計されているので、暗号化アルゴリズムは通常(直感に反する)ではありません。ただし、具体的には[〜#〜] gcm [〜#〜]モードでAESを使用しているため、改ざん防止暗号化(「認証された暗号化」)をすでに取得しているため、これは特に問題ではありません。
  2. 着信JWTを検証するときは、有効と考えるものに注意してください。たとえば、{"sub": "me"、 "alg": "none"}を使用してサービスを呼び出すことができます。そのJWTはある意味で有効ですが、受け入れたくないものです。
  3. JWTはドラフトであり、まだ標準ではないため、変更される可能性があります。十分に変更されている場合、使用しているライブラリは、コードとの互換性を損なう方法で変更する必要がある場合があります。
  4. サーバー側の状態を保存できない場合、ユーザーがログアウトしたときにJWTを無効にする方法はありません。実際には、サービスにはログアウト機能がありません。これは、特に有効期限をあまりにも長く設定しすぎると、セキュリティ上の問題になる可能性があります。
  5. 有効期限の設定が早すぎると、ユーザーはまだログインしているが有効なJWTがないという問題が発生する可能性があります。これにより、扱いにくいエラー処理やユーザーワークフローの問題が発生する可能性があります。

サーバーからデータベースにアクセスできないとのことですが、実際のログインは別の場所で処理されていると思います。ユーザーがログインしたことをサーバーがどのように認識しているかは言いませんでした。サービスとユーザーがログインしたことを知っていることとの関係に対するユーザーの認識によっては、上記の最後の2つのポイントは意味がないかもしれません。

52
gatkin

重要なデータがない場合は、データの整合性を維持するだけで済みます。署名(JWS)トークンで十分です。

署名を行うだけの場合は、HMAC SHA-256で問題ありません。トークンの有効期限を設定し、ユーザーが手動でログアウトしたかどうかを確認することを忘れないでください(その場合、トークンを無効にします)。有効期限とログアウトを考慮に入れると、それほど心配する必要はありません(アルゴリズム的には)。単一のセッションの間に誰かがSHA-256をクラックする可能性は比較的低い(すべきです)(妥当な間隔で再認証が必要であると想定します)。

いつものように、署名するコンテンツ(ユーザー名、アカウントタイプなど)を提供することを確認してください。ユーザー定義データの署名を許可しないでください。そうしないと、危険な状況になる可能性があります。

免責事項:この投稿は厳密に私の意見です。私は自分の回答の信頼性や適合性について何も主張していません。常にセキュリティの専門家に相談して、特定のセキュリティ実装の問題について話し合う必要があります。これは純粋に教育的なものです。

10
Daisetsu