redirect_uri 検証の仕様比較
redirect_uri(リダイレクト URI)の検証方法は、OAuth 2.0 の歴史とともに厳格化されてきました。このドキュメントでは、各仕様における検証ルールの違いを比較し、セキュリティと利便性のバランスを考察します。
第1部: 概要
なぜ redirect_uri の検証が重要なのか?
redirect_uri は認可コードやトークンの送信先です。検証が甘いと、攻撃者が認可コードを自分のサーバーに送らせることができます。
正常なフロー:
ユーザー → 認可サーバー → https://legitimate-app.com/callback?code=xxx
攻撃(オープンリダイレクタ):
ユーザー → 認可サーバー → https://attacker.com/steal?code=xxx
↑ 認可コードが攻撃者に渡る
検証の厳格さスペクトラム
緩い 厳格
│ │
▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
│ 登録なし │ │ 部分一致 │ │ 完全一致 │ │ 完全一致 │ │ 完全一致 │
│ (非推奨) │ │ (RFC6749) │ │ (OIDC) │ │ + 制約 │ │ + 制約 │
│ │ │ │ │ │ │ (BCP) │ │ (FAPI) │
└──────────┘ └──────────┘ └──────────┘ └──────────┘ └──────────┘
利便性: 高 ◄─────────────────────────────────────────────────► 低
安全性: 低 ◄─────────────────────────────────────────────────► 高
RFC 3986: URI 比較の基礎
redirect_uri の検証を理解するには、まず URI の仕様である RFC 3986 を理解する必要があります。
URI の構造
https://user:pass@example.com:8080/path/to/resource?query=value#fragment
└─┬─┘ └───┬───┘ └────┬────┘└─┬┘└──────┬────────┘└─────┬─────┘└───┬───┘
scheme userinfo host port path query fragment
└──────────┬──────────┘
authority
| 要素 | 説明 | 大文字小文字 | RFC 3986 セクション |
|---|---|---|---|
| scheme | プロトコル(http, https) | 区別なし | 3.1 |
| userinfo | ユーザー情報(非推奨) | 区別あり | 3.2.1 |
| host | ホスト名または IP | 区別なし | 3.2.2 |
| port | ポート番号 | - | 3.2.3 |
| path | リソースパス | 区別あり | 3.3 |
| query | クエリパラメータ | 区別あり | 3.4 |
| fragment | フラグメント識別子 | 区別あり | 3.5 |
URI の比較方法(RFC 3986 Section 6)
RFC 3986 では URI を比較する 4 つの方法を定義しています。
1. Simple String Comparison(単純文字列比較)
Two URIs are equivalent if they are identical character-by-character.
最も厳格な方法。文字単位で完全に一致するかを確認。
比較対象: https://example.com/callback
✅ 一致: https://example.com/callback
❌ 不一致: https://EXAMPLE.COM/callback ← 大文字
❌ 不一致: https://example.com:443/callback ← デフォルトポート明示
❌ 不一致: https://example.com/callback/ ← 末尾スラッシュ
❌ 不一致: https://example.com/Callback ← パスの大文字小文字
OAuth 2.0 Security BCP(RFC 9700)はこの方法を要求。
2. Syntax-Based Normalization(構文ベース正規化)
正規化してから比較。以下の変換を行う:
| 正規化 | 例 |
|---|---|
| スキームを小文字化 | HTTPS: → https: |
| ホストを小文字化 | Example.COM → example.com |
| パーセントエンコードを大文字化 | %2f → %2F |
| 不要なパーセントエンコードを解除 | %41 → A |
空パスを / に | https://example.com → https://example.com/ |
| デフォルトポートを削除 | :443 → (削除) |
正規化前: HTTPS://Example.COM:443/Path
正規化後: https://example.com/Path
正規化前: https://example.com
正規化後: https://example.com/
この方法を使うと、以下が同一視される:
https://example.com/callback
https://EXAMPLE.COM/callback ← ホストは正規化で同一
https://example.com:443/callback ← デフォルトポートは正規化で削除
3. Scheme-Based Normalization(スキームベース正規化)
スキーム固有のルールを適用。例えば HTTP では:
- 空パスを
/に変換 - デフォルトポート(80/443)を削除