以下の内容はhttps://tech-blog.sterrasec.com/entry/2026/02/10/160043より取得しました。


その「ソーシャルログイン」は大丈夫?OAuth/OIDC実装の3つの落とし穴

代表の小竹(aka tkmru)です。

Googleでログイン」などのソーシャルログインは、今やWebサービスの標準機能です。 この機能は一般的に、認可の仕組みであるOAuthの上に、ユーザーの身元確認を行うためのOpenID Connect(OIDC)というプロトコルを重ねることで実現されています。

RFC 9700(Best Current Practice for OAuth 2.0 Security)およびIETFによって策定が進められている OAuth 2.1の登場により、推奨されている実装は変化しつつあります。

本題に入る前に、次のセルフチェックを試してみてください。

  • [ ] 認可コードフローにPKCEを適用しているか
  • [ ] IDトークンの aud (Audience)を検証し、「他サービス向けのトークン」を拒否しているか
  • [ ] nonce パラメータを送信するだけでなく、返却された値の一致検証をロジックとして実装しているか
  • [ ] ライブラリ任せにせず、許可する署名アルゴリズム(RS256等)を明示的に指定しているか

もし即答できない項目があれば、本記事が参考になるはずです。

1. 認可コード横取り攻撃とPKCE

まずは、OAuth 2.0における通信経路の安全性についてです。従来、CSRF対策としてランダムな文字列を用いた state パラメータの検証が広く使われてきました。state は、リクエストとコールバックが同一セッションであることを検証するには有効です。

一方で、state は認可コードそのものの漏洩には対処できません。スマートフォンアプリやSPAでは、カスタムURLスキームのハイジャック等によって、認可コードが正規のクライアント以外に渡ってしまう「認可コード横取り攻撃(Authorization Code Interception Attack)」のリスクがあります。

この脅威に対処する仕組みがPKCE(Proof Key for Code Exchange, RFC 7636)です。

OAuth 2.1ではPKCEは「必須」へ

かつてPKCEは、スマートフォンアプリなどの「パブリッククライアント」が、認可コードの横取りを防ぐための仕組みとして紹介されていました。しかし、OAuth 2.1では全てのクライアントでPKCEが必須(REQUIRED)とされています。

PKCEの仕組み

PKCEでは、認可リクエストを行った端末とコードを交換する端末が同一であることを暗号学的に保証します。

  1. 認可リクエスト時: クライアントが code_verifier(ランダムな文字列)を生成し、そのハッシュ値 code_challenge を認可サーバに送ります。
  2. トークン交換時: クライアントは元の code_verifier を送信します。
  3. 検証: 認可サーバは受け取った code_verifier をハッシュ化し、最初に受け取った code_challenge と一致するかを検証します。

攻撃者が認可コードを横取りできたとしても、code_verifier を知らなければトークン交換に成功しないため、攻撃は成立しません。利用しているOAuth/OIDCライブラリの設定を確認し、PKCEが有効になっているかを確認してください。

2. IDトークンの検証不備(OIDC)

ここからは、ユーザー認証(ログイン)の中核となるOpenID Connect(OIDC)の層について解説します。OIDCでは、認可サーバから認証結果を含むIDトークン(JWT形式)が発行されます。アプリ側はこのIDトークンを検証することで「誰がログインしたか」を判断しますが、この検証が不十分な場合、トークンの偽造や差し替えによるなりすましが成立し得ます。

そもそもIDトークンの中身とは?

IDトークンはJWT(JSON Web Token)形式で発行されます。実際にアプリケーションが受け取るのは、次のように .(ドット)で区切られた3つのBase64エンコードされた文字列です。

<ヘッダのBase64文字列>.<ペイロードのBase64文字列>.<署名のBase64文字列>

これをBase64でデコードすると、次のようなJSONデータが現れます。検証の実装漏れを防ぐには、この中身を正しく理解する必要があります。

// 1. ヘッダ: 署名アルゴリズムなどが記載
{
  "alg": "RS256",   // ←【検証】ここが "none" になっていないか?
  "kid": "XXXX",
  "typ": "JWT"
}

// 2. ペイロード: ユーザー情報や検証用データ
{
  "iss": "https://accounts.google.com",  // ←【検証】発行者は正しいか?
  "sub": "XXXX150350006150715113082367",
  "aud": "123456789-example-id.apps.googleusercontent.com", // ←【検証】自社アプリ宛か?
  "iat": XXXX161616,
  "exp": XXXX165216,                     // ←【検証】有効期限内か?
  "nonce": "XXXX6_WzA2Mj",               // ←【検証】リクエスト時の値と一致するか?
  "email": "taro.sterra@sterrasec.com"
}

// 3. 署名: 改ざん検知用の署名データ
// ヘッダ + ペイロードを秘密鍵で署名したものをBase64エンコードしたもの

署名アルゴリズムの明示的な指定

IDトークンの検証では、まず署名が正しいことを確認する必要があります。ここで注意すべき点として、JWTライブラリの中には、トークンのヘッダに記載された alg の値をそのまま信頼して検証アルゴリズムを決定するものがあります。攻撃者が algnone に書き換えた場合、署名検証自体がスキップされ、任意のクレームを持つトークンが受け入れられてしまいます。これを防ぐには、ライブラリの設定で許可するアルゴリズムをRS256等に明示的に限定する必要があります。

「署名が正しい」だけでは不十分

署名検証が正しく行われていても、それだけでは防げないのが「Confused Deputy(混乱した代理人)」問題です。 攻撃シナリオは次のようになります。

  1. 攻撃者が、自作の悪意あるアプリ(例:「無料占いアプリ」)を作る。
  2. ユーザーがそのアプリに「Googleでログイン」する。
  3. 攻撃者は自作アプリのサーバ側で、ユーザーに対して発行されたIDトークン(ただし「無料占いアプリ」宛て)を取得する。
  4. 攻撃者が、このトークンを標的企業のサービス(例:「社内ポータル」)に送信してログインを試みる。

もし貴社のサービスが「署名」と「発行者(iss)」しか見ていない場合、このトークンは「Googleが発行した正当な署名付きトークン」であるため、ログインが成功してしまいます。

audの検証

この攻撃への直接的な対策は aud の検証です。ここには「このトークンはどのアプリ(Client ID)のために発行されたか」が記載されています。

{
  "iss": "https://accounts.google.com",
  "sub": "1234567890",
  "aud": "YOUR_CLIENT_ID",
  "iat": XXXX239022,
  "exp": XXXX242622
}

検証ロジックにおいて、aud の値が自社のClient IDと完全一致することを確認してください。 ライブラリによってはデフォルトでチェックしないものもあるため、明示的な設定が必要です。

3. nonceパラメータの検証がされていないケース

OIDCには、先述の state とは別に、nonce(ナンス)パラメータがあります。 よくある誤解は、「リクエストに nonce を含めたから安心」と考えてしまうことです。しかし、この検証が不十分な場合、トークンの差し替えやリプレイによるなりすましが成立し得ます。

  • state (OAuth層): ブラウザのセッション単位でCSRFを防ぐ(リクエストとコールバックの整合性)。
  • nonce (OIDC層): IDトークン単位でリプレイ攻撃を防ぐ(トークンとリクエストの整合性)。

なぜ nonceが必要なのか

nonce は、発行されたIDトークンが「今回の自分のリクエストに対して発行されたものか」を保証するための値です。 先ほどのJWTの例にも nonce が含まれていましたが、ここの値が「自分がリクエスト時に送ったランダム値」と一致するか確認します。

検証の流れは次のとおりです。

  1. ランダムな値を生成し、ブラウザのセッション(Cookie等)に保存する。
  2. 認可リクエストに nonce パラメータとして含める。
  3. 戻ってきたIDトークン内の nonce を取り出す。
  4. IDトークン内の値とセッションに保存した値が一致するか比較する。

フローによる攻撃経路の違い

Implicit FlowやHybrid Flowでは、IDトークンがURLフラグメント等のフロントチャネル経由で返却されます。この場合、攻撃者がネットワーク上やブラウザ履歴からIDトークンを取得し、被害者のログイン処理に注入するリプレイ攻撃が成立し得ます。nonce の検証を行っていなければ、この攻撃を検知できません。

一方、現在主流のAuthorization Code Flowでは、IDトークンはバックチャネル(トークンエンドポイントからのレスポンス)で取得されるため、上記の経路による漏洩リスクは低くなります。ただし、Authorization Code Flowにおいても nonce は認可コード注入攻撃(Authorization Code Injection)への対策として機能します。攻撃者が盗んだ認可コードを被害者のセッションに注入した場合、トークンエンドポイントから返却されるIDトークン内の nonce が被害者のセッションに保存された値と一致しないため、攻撃を検知できます。

仕様上の位置づけ

OIDCの仕様(OpenID Connect Core 1.0)において、Implicit Flowでは nonce の使用が必須(REQUIRED)とされています。Authorization Code Flowでは任意(OPTIONAL)とされていますが、リクエストに nonce を含めた場合は検証が必須となります。

コードを確認し、id_token.nonce === session.nonce に相当する比較ロジックが存在するか、あるいは使用しているライブラリがこの検証を自動で行っているかを確認してください。

まとめ

認証フローにおいては「ライブラリを使っているから安全」「大手IdPを使っているから大丈夫」と判断していても、考慮漏れ一つで脆弱性が生まれることがあります。「とりあえずログインできたからOK」という状態でリリースすると、本記事で取り上げたようなセキュリティホールが残りやすくなります。

これらの脆弱性は認証フローのロジックに起因するものであり、一般的なWebアプリケーションの自動スキャンツールでは検知が困難です。OAuthやOIDCの仕様を熟知した診断員が、実際の認証フローを追いながら検証することで初めて発見できるケースが少なくありません。

弊社では、本記事で解説した3つの観点を含む、OAuth/OIDC認証基盤に対応した脆弱性診断を提供しています。「自社の認証基盤に不安がある」「リリース前に第三者の視点で徹底的にチェックしたい」という方は、ぜひお気軽にご相談ください。




以上の内容はhttps://tech-blog.sterrasec.com/entry/2026/02/10/160043より取得しました。
このページはhttp://font.textar.tv/のウェブフォントを使用してます

不具合報告/要望等はこちらへお願いします。
モバイルやる夫Viewer Ver0.14