OAuth 2.0の「リダイレクト地獄」を断ち切る:Open Redirectを許さない実装の極意
現場でコードをレビューしていると、いまだに「なぜこんな単純な穴を放置するんだ?」と頭を抱える瞬間がある。特にOAuth 2.0の `redirect_uri` 周りだ。
「まあ、テスト環境だし適当なURLでいいか」「検証用だから正規表現で緩くしておこう」。そんな甘い判断が、数ヶ月後に大規模なアカウント乗っ取りの踏み台にされる。今回は、攻撃者が好んで突く「Open Redirect」のメカニズムと、それを実務で完全に封じ込めるための実装論を叩き込む。
—
1. なぜ「甘い検証」が命取りになるのか
OAuth 2.0のフローにおいて、`redirect_uri` は認可コードをクライアントに届けるための命綱だ。ここを攻撃者が操作すると、本来のアプリではなく、攻撃者が用意した悪意あるサーバーへ認可コード(またはアクセストークン)を横流しさせることができる。
攻撃者の視点:PoCの現実
攻撃者は、検証が甘い `redirect_uri` を見つけると、以下のようなURLをユーザーに踏ませる。
https://auth.example.com/authorize?
client_id=123&
redirect_uri=https://auth.example.com/login?next=https://malicious.com&
response_type=code
もしサーバー側が `redirect_uri` を「ドメインの先頭部分だけ」で判定していたり、正規表現がザルだったりすると、攻撃者は `next` パラメータを悪用して、被害者の認証情報を盗み取る釣堀へと誘導する。これを「Open Redirect」と呼ぶ。
—
2. 唯一の正解:厳密な「ホワイトリスト方式」
多くのエンジニアが陥る罠は「柔軟性」を求めてワイルドカードや複雑な正規表現を書くことだ。セキュリティの世界で、認証に関わる部分に「柔軟性」は不要。「完全一致(Exact Match)」こそが、唯一無二の防御策だ。
実装サンプル:Python (Flask) での厳密な検証
ホワイトリストをハードコーディング、あるいは環境変数で管理し、リクエストされた `redirect_uri` がリストに含まれているかを確認する。
from urllib.parse import urlparse
承認済みのリダイレクトURIリスト(末尾スラッシュまで含めて完全一致させる)
ALLOWED_REDIRECT_URIS = [
“https://app.example.com/callback”,
“https://mobile-app.example.com/auth”
]
def validate_redirect_uri(redirect_uri):
# 1. 空チェック
if not redirect_uri:
return False
# 2. ホワイトリストとの完全一致比較
# 含まれていなければ即座に拒否
if redirect_uri not in ALLOWED_REDIRECT_URIS:
# ログには必ず詳細を残すこと。セキュリティ監視の要になる
print(f”セキュリティ警告: 不正なリダイレクト試行検知 {redirect_uri}”)
return False
return True
使用例
def authorize_endpoint(request):
requested_uri = request.args.get(‘redirect_uri’)
if not validate_redirect_uri(requested_uri):
return “Invalid redirect_uri”, 400
# 認可処理へ進む…
—
3. インフラ層で防御を固める(Nginx編)
アプリ側での実装が漏れた時のための「最後の砦」として、Webサーバー層でもフィルタリングをかけるのがプロの流儀だ。Nginxの設定で、特定のパス以外へのリダイレクトを物理的にブロックする例を紹介する。
Nginxで特定のリダイレクト先以外を拒否する設定例
location /oauth/authorize {
# クエリパラメータ redirect_uri を精査
if ($arg_redirect_uri !~ “^https://(app|mobile-app)\.example\.com/.”) {
return 403; # ホワイトリストにないドメインなら即ブロック
}
proxy_pass http://backend_server;
}
※注:正規表現は複雑化しがちなので、可能な限りアプリケーション側でのロジック制御を優先すべきだ。
—
4. プロとして守るべき3つの鉄則
現場でトラブルに巻き込まれないために、以下のルールをチームの憲法として刻んでくれ。
1. 正規表現による「部分一致」を禁止する: `example\.com` という正規表現は `example.com.attacker.com` にもマッチする。これは致命的だ。必ず「完全一致」で行うこと。
2. デフォルトを拒否に設定する: ホワイトリストに含まれていないものは、例外なくエラー(400 Bad Request)を返す。
3. ログに「攻撃者の痕跡」を残す: 誰が、いつ、どの不正なURIへリダイレクトしようとしたかをログ出力し、異常検知のトリガーにする。
—
最後に
セキュリティは「これくらいなら大丈夫だろう」という慢心が生まれた瞬間に破られる。`redirect_uri` のバリデーションは地味な作業だが、ここを完璧にガードすることは、君たちの開発したプロダクトの信頼性を守ることに直結する。
次のスプリントから、自分の担当箇所の `redirect_uri` が「完全一致」で検証されているか、ぜひ確認してみてほしい。怪しい実装があれば、迷わずその場でリファクタリングだ。それが、君が「信頼されるエンジニア」になるための最短ルートだ。

コメント