1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【セキュリティ】OAuth 2.0:redirect_uri ハイジャック攻撃の仕組みと防御

1
Last updated at Posted at 2025-11-17

はじめに

OAuth は現代アプリの認証基盤ですが、設定を誤ると簡単に破られます。
その代表例が redirect_uri の脆弱性を悪用したトークンハイジャック攻撃
本記事では、攻撃の仕組み、正常フロー、client_id の扱い、そして防御策を実務レベルでまとめます。


1. OAuth の基本:redirect_uri とトークンの受け渡し

OAuth 2.0 の Authorization Code Flow では:

  1. ユーザーがログイン
  2. Authorization Server が認可コード(code)を発行
  3. code を redirect_uri に送る
  4. クライアントが code を使ってアクセストークンを取得

このうち redirect_uri は “認可コードの送り先” です。
だからこそ、攻撃者に奪われるとコードも奪われるという非常に重要なパラメータ。


2. redirect_uri の役割と危険性

redirect_uri の役割

  • 認可コードを送り返す住所(callback先)
  • 開発者コンソールに事前登録された URI と “完全一致”しなければならない

なぜ危険なのか?

登録されている redirect_uri のうち ひとつでも攻撃者が所有できる URI があれば:

Authorization Server は「合法なリダイレクト先」と判断してしまう。

結果、攻撃者は Authorization Code を直接受け取れる

OAuth 最大の事故はここで起きます。


3. redirect_uri ハイジャック攻撃の流れ(TryHackMe の例)

TryHackMe のシナリオでは、
http://dev.bistro.thm:8002/ が redirect_uri として登録済みで、
攻撃者はこのドメイン(サブドメイン)を完全に支配しています。

つまり:

攻撃者は redirect_uri に自分のページを設定できる状態。

たとえばこんな攻撃用 HTML を配置するだけ:

<form action="http://coffee.thm:8000/oauthdemo/oauth_login/" method="get">
    <input type="hidden" name="redirect_uri" value="http://dev.bistro.thm:8002/malicious_redirect.html">
    <input type="submit" value="Hijack OAuth">
</form>

ユーザーがログインすると、認可コードが
攻撃者のページに ?code=XXXX として飛んでくる。

攻撃者は JS で回収:

const code = new URLSearchParams(window.location.search).get('code');
console.log("Intercepted Authorization Code:", code);

そのコードを /token(または Demos の /callbackforflag)に送れば
攻撃者は 正式なアクセストークン を取得できる。


4. どうやって “redirect_uri が攻撃者ドメインになったのか?”

攻撃者が redirect_uri を勝手に書き換えたわけではありません。
本質はこうです:

1) 開発者が redirect_uri として登録していた

例:

http://dev.bistro.thm:8002/callback

2) そのドメインが攻撃者に乗っ取られた

パターンは現実でも多い:

  • 古い開発用サブドメインが放置される
  • DNS が解放され、攻撃者が再取得
  • Wildcard redirect_uri(*.example.com)が設定されていた
  • SaaS の redirect_uri を登録したまま権限が切れた

TryHackMe では 演習のため最初から攻撃者が自由に操作可能という設定。

つまり攻撃者は:

“登録済み redirect_uri のうち、自分が所有するものを使っただけ”

これが OAuth の盲点。


5. client_id(ClientString)はどこから来た?盗まれたのか?

ここが誤解しやすいが重要。

結論

client_id は盗まれない。盗む必要もない。
client_id は “公開情報” だから。

実際:

  • OAuth のログイン URL
  • フロントエンドの JavaScript
  • HTML 内のフォーム
  • SPA の設定ファイル
  • WebInspector(Network) の通信内容

…どこからでも見える。

Google OAuth の login URL も client_id 丸見えです。

client_id=123abc.apps.googleusercontent.com

そして client_id が公開でも問題ない理由は:

OAuth の安全性は client_id に依存していない。
redirect_uri の厳密一致と PKCE が本体。

だから攻撃成立の条件として client_id は何の壁にもならない


6. 正常な OAuth 2.0 Authorization Code Flow(安全版)

攻撃シナリオと比較するため、正常フローをまとめます。

正常フロー(安全)

  1. クライアントが /authorize に redirect
  2. サーバがユーザーの認証 & 同意
  3. 認可コードを 登録済み redirect_uri に返す
  4. クライアントがバックチャネルで /token に code + PKCE verifier を送信
  5. access_token / refresh_token を受け取る
  6. Resource Server にアクセス

要点:

  • redirect_uri は完全一致
  • code は一度しか使用できない
  • PKCE(S256)が必須
  • state 検証で CSRF 防止
  • クライアントとサーバの通信は HTTPS

7. redirect_uri ハイジャック攻撃が成立する理由

以下の3条件のうち 1つでも破られると OAuth は終わる

(1) redirect_uri が攻撃者のドメインだった

→ 本来の callback ではなく、攻撃者のページへ code が届く

(2) PKCE が無い、または検証されない

→ 奪った Authorization Code がそのまま使われてしまう

(3) redirect_uri を開発者が正しく管理していない

→ 古いサブドメイン・ワイルドカード設定がそのまま登録されていた

攻撃者は redirect_uri を支配できた時点で勝利です。


8. 防御方法(実務レベル)

✔ redirect_uri は 1つだけ、完全一致

最も重要。ワイルドカード禁止。

✔ PKCE(S256)を必須化

コードを盗まれても再利用できなくなる。

✔ state / nonce を厳密に検証

CSRF・リプレイ防止。

✔ 古いサブドメインの整理(非常に重要)

企業で実際に最も多い事故。

✔ Authorization Code を 1 回だけ使用可能にする

二重利用を検知して即エラーにする。


まとめ

redirect_uri は「認可コードの送り先」。
ここを攻撃者に握られた瞬間、OAuth は破綻する。

client_id は公開情報であり、攻撃者が盗む必要はない。
問題は redirect_uri の設定管理 が甘い点。

つまり:

OAuth の安全性=redirect_uri の管理+PKCE の有無

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?