「ログインしたはずが、秘密の鍵を盗まれた?」OAuthのリダイレクトを悪用する罠を解き明かす
こんにちは!現場で泥臭いインシデント対応を続けているセキュリティエンジニアです。
今日は、Web開発の現場で避けては通れない「OAuth 2.0」と、そこにある「リダイレクトの罠」についてお話しします。難しそうに聞こえますが、実は「家の鍵」を渡すときの手順に置き換えると、すごくシンプルに見えてくるんですよ。
一歩ずつ、一緒に学んでいきましょう!
—
1. OAuthとリダイレクト:家の鍵を渡す「受渡場所」の話
OAuth 2.0は、例えば「Googleアカウントを使って、別のサービスにログインする」ときなどに使われる仕組みです。このとき、サービスは「あなたが本人であること」を証明するデジタルな鍵(アクセストークン)を受け取ります。
問題は、「その鍵をどこに届けるか」です。
通常、認可サーバー(Googleなど)は、ログインが終わった後、あなたのブラウザを「あらかじめ登録されたサイト(リダイレクト先)」へ自動的に転送します。このとき、URLのパラメータに「鍵(トークン)」をくっつけて送るのが一般的です。
ここで起こる「泥棒の悪だくみ」
もし、この「どこへ転送するか」という指定がガバガバだったらどうなるでしょう?
泥棒(攻撃者)は、あなたを巧妙なリンクで釣って、「ログインが終わったら、俺の作った偽サイトへ鍵を送れ!」と指示を送ります。認可サーバーがこれを信じてしまうと、あなたの「鍵」は、あっという間に泥棒のサーバーへ転送されてしまいます。これが「オープンリダイレクタ」を悪用したトークン窃取攻撃です。
—
2. なぜ「ホワイトリスト」が最強の鍵穴なのか?
「じゃあ、どこに転送してもいいようにすれば便利じゃない?」と思うかもしれません。しかし、それは「家の鍵をどこでも好きな場所に置いていいよ」と言っているのと同じで、非常に危険です。
ここで登場するのが「ホワイトリスト方式」です。これは、「あらかじめ許可リストに載っている場所(住所)にしか、絶対に鍵を届けない」という厳格なルールです。
悪い例(やってはいけない検証)
// ダメな例:ユーザーが指定したURLをそのままリダイレクト先に使う
const redirectUri = req.query.redirect_uri;
// 攻撃者が “?redirect_uri=https://evil-hacker.com” と送ってきたら、鍵が盗まれる!
res.redirect(redirectUri);
良い例(ホワイトリストで守る)
// 許可されたリダイレクト先リスト
const ALLOWED_REDIRECT_URIS = [
“https://myapp.com/callback”,
“https://myapp.com/dashboard”
];
const redirectUri = req.query.redirect_uri;
// リストに含まれているか厳密にチェックする
if (ALLOWED_REDIRECT_URIS.includes(redirectUri)) {
res.redirect(redirectUri);
} else {
// 許可されていない場所には絶対に行かせない!
res.status(400).send(“不正なリダイレクト先です。”);
}
ポイントは、「ユーザーの言いなりにならないこと」です。「あなたが指定した場所は、うちの許可リストに載っていますか?」と、毎回厳しく確認することが、泥棒から身を守る最大の防御になります。
—
3. なぜXSSと組み合わせるとさらに危険なのか?
ところで、皆さんは「XSS(クロスサイトスクリプティング)」という言葉を聞いたことはありますか? 画面に悪意のあるスクリプトを埋め込まれる攻撃のことです。
実は、この「リダイレクトURIの検証不備」と「XSS」が組み合わさると、攻撃者はさらに簡単に鍵を盗めるようになります。XSSでページの挙動を書き換え、リダイレクト先に細工をしたり、ブラウザのメモリから直接トークンを抜き取ったりするのです。
防御の基本:CSP(コンテンツセキュリティポリシー)
XSSから身を守るために、ぜひ「CSPヘッダー」を設定してください。これは、Webサイトに対して「このサイトで実行していいスクリプトはこれだけだよ!」というルールをブラウザに伝える防犯アラームのようなものです。
HTTPレスポンスヘッダーの例:
Content-Security-Policy: default-src ‘self’; script-src ‘self’ https://trusted.cdn.com;
default-src 'self':基本的に自分のサイト内のソースしか許可しない。script-src:信頼できるスクリプトの場所だけを指定する。
これを入れておくだけで、万が一どこかに脆弱性があっても、外部から持ち込まれた怪しいスクリプトが動くのを防げる確率がグッと上がります。
—
最後に:セキュリティは「疑うこと」から始まる
開発者の皆さんに伝えたいのは、「自分のコードを信じすぎないでほしい」ということです。
- ユーザーから送られてくるURLは、常に「嘘をついているかもしれない」と疑う。
- リダイレクト先は、必ず「あらかじめ決めた安全な場所」だけに限定する。
- 複雑なことをしようとせず、フレームワークが提供する標準のOAuthライブラリを正しく使う。
セキュリティ対策は、完璧を目指すのではなく、「泥棒が入りにくい家(コード)を作ること」の積み重ねです。今日から一つずつ、リダイレクトURIのチェックを見直してみませんか?
皆さんのプロダクトが、ユーザーにとって安全で信頼できる場所であり続けることを応援しています!何かあれば、いつでも相談してくださいね。
コメント