こんにちは!Webアプリケーションを作ったり、会社のセキュリティを守る担当になったばかりの頃って、「OAuth 2.0」とか「OIDC(OpenID Connect)」なんて聞くだけで、なんだか難しそう……って身構えちゃいますよね。
でも大丈夫です!今回は、私たちの身の回りにある「合鍵」と「泥棒の防犯対策」に例えながら、OAuth 2.0のちょっと怖い「認可コード横取り攻撃」と、それをピシャリと防ぐ「PKCE(ピクシー)」、そして「リダイレクトURIの厳格な検証」について、一歩ずつ優しく紐解いていきましょう。
実務ですぐに使えるコードや設定例も交えて解説するので、ぜひ最後まで読んでいってくださいね!
—
1. 身の回りの防犯に例えるOAuthと認可コードの仕組み
まずは、OAuth 2.0やOIDCが普段私たちの生活でどう使われているかを見てみましょう。
よくある「Googleアカウントでログイン」や「SNS連携」を思い出してみてください。あれは、あなたがいつも使っているアプリ(例:写真加工アプリ)に、Googleさん(IDプロバイダー)が「この人は間違いなく本人の〇〇さんですよ」とお墨付きを与えてくれる仕組みです。
これを「マンションの合鍵システム」に例えてみます。
1. 写真加工アプリ(クライアント)が、あなたにこう言います。「Googleさんからあなたの写真フォルダを見せてもらう許可を貰ってきてくれますか?」
2. あなたはGoogle(認可サーバー)の部屋に行って、「あのアプリに写真を見せる許可をください!」と頼みます。
3. Googleさんは「分かりました。では、これを見せれば一回だけ写真フォルダを開けられる『特別な引換券(認可コード)』をあげるので、それをアプリに渡してください」と言って、紙切れを渡してくれます。
4. あなたはその引換券をアプリに渡し、アプリはその引換券をGoogleさんに渡して、無事に写真フォルダへの鍵(アクセストークン)をゲットします。
この手順の中で、アプリが受け取る「引換券(認可コード)」が今回の主役です。
—
2. 恐ろしい「認可コード横取り攻撃」の正体
さて、ここで問題が発生します。もし、あなたがGoogleさんからもらった「引換券」を、悪意ある偽物のアプリ(泥棒)にうっかり盗まれてしまったらどうなるでしょうか?
これが「認可コード横取り攻撃」です。
泥棒の手口
泥棒は、スマホやパソコンの中に別の悪質なアプリを仕込んでおき、あなたが本物のアプリのためにもらった「引換券」の情報をこっそり横取りします。そして、泥棒はその引換券を先回りしてGoogleさんに突きつけ、「ほら!引換券を持ってきたから鍵(アクセストークン)をちょうだい!」と要求するのです。
郵便受けから手紙が盗まれるようなもので、引換券さえ持っていれば、Googleさんは「本人が来たんだな」と勘違いして鍵を渡してしまいます。これが、モバイルアプリやデスクトップアプリなどで特に起こりやすい危険な罠でした。
—
3. 救世主「PKCE(Proof Key for Code Exchange)」の仕組み
「じゃあ、引換券が盗まれたらおしまいなの?」と思いますよね。そこで登場するのが、今回のヒーローである PKCE(読み方:ピクシー) です!
難しそうな名前ですが、やっていることはとってもアナログで確実な防犯対策です。例えるなら、「合言葉と秘密の暗号錠」です。
1. お出かけ前の準備(コードベリファイアーとチャレンジの作成)
本物のアプリは、出発する前に自分で「秘密の合言葉(code_verifier)」をこっそり作ります。そして、その合言葉をカプセルに入れてグチャグチャに混ぜた「暗号のヒント(code_challenge)」を作ります。
2. Googleさんにお願いする時
アプリはGoogleさんに「ログインの許可をください!ちなみに、これが私の『暗号のヒント』です」と、ヒントだけを先に預けます(合言葉そのものは絶対にアプリの外に出しません!)。
3. 引換券の交換時
泥棒が引換券を盗み出してGoogleさんに渡そうとしても、Googleさんはこう言います。「おっ、引換券だね。じゃあ、最初にもらった『秘密の合言葉』を教えてごらん?」
泥棒は合言葉を知らないので、ここでジィーエンド!本物のアプリしか本当の合言葉(code_verifier)を知らないため、泥棒を完璧にシャットアウトできるというわけです。
実際のパラメーターのイメージ
実務のコードを書くとき、リクエストには次のようなパラメーターを付与します。
GET /authorize?
response_type=code&
client_id=your_client_id&
redirect_uri=https://app.example.com/callback&
code_challenge=E9Melhoa2OwvFrGMTJguCHaoeK1t8URWbuGJSstw-cM&
code_challenge_method=S256
code_challenge: 秘密の合言葉をハッシュ化したもの(ヒント)。code_challenge_method=S256: ヒントを作るための暗号アルゴリズム(SHA-256を指定するのが現代の標準です)。
そして、引換券をアクセストークンに交換する最後のステップで、いよいよ本物の合言葉(code_verifier)を送信します。
POST /token
Host: auth.example.com
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code
&client_id=your_client_id
&code=SplxlOBeZQQYbYS6WxSbIA
&code_verifier=dBjftJeZ4CVP-mW82K10uyhoSuNMomVZSgV4_NPaSKo
&redirect_uri=https://app.example.com/callback
このように、「引換券+最初に自分で決めた合言葉のセット」で確認を取ることで、万が一コードが盗聴されても悪用を防ぐことができるのです。
—
4. もう一つの鉄壁の盾:「リダイレクトURIの厳格な検証」
PKCEと並んで絶対に忘れてはいけないのが、「リダイレクトURI(引換券の届け先)」の厳格な検証です。
先ほどのマンションの例で言うと、Googleさんが引換券を渡すとき、「あなたの届け先(住所)は https://app.example.com/callback ですよね?」と事前に登録された住所にしか荷物を送らないようにする仕組みです。
もし、ここがいい加減だとどうなるでしょうか?
攻撃者が「私の怪しい住所(https://attacker.example.com/steal)に引換券を送って!」と嘘の住所を登録できたとしたら、Googleさんはホイホイとそこに引換券を送ってしまいますよね。
開発時の注意点と対策
実務のサーバーサイド(例えばPHPやNode.jsなど)で認可サーバーを構築・設定する際は、以下のポイントを絶対に守りましょう。
1. 完全一致(Exact Matching)で検証する
- ワイルドカード(例:
https://*.example.com/callback)を安易に使わないでください。攻撃者にサブドメインを乗っ取られたら終わりです。
2. プレフィックスや部分一致は使わない
- 「前方一致していればOK」なんて甘い実装にすると、攻撃者が細工したURLを通してしまう原因になります。必ず完全一致で弾きましょう。
以下は、PHPでリダイレクトURIが登録済みの安全なものかチェックするイメージコードです。
<?php
// あらかじめ認可サーバー側(データベースなど)に登録されている信頼できるURIのリスト
$allowed_redirect_uris = [
"https://app.example.com/callback",
"https://app.example.com/settings/callback"
];
// ユーザー(アプリ)から送られてきたリダイレクトURI
$client_redirect_uri = $_GET['redirect_uri'] ?? '';
// 厳格な完全一致(In_arrayの第3引数にtrueを指定して厳密に比較)でチェック
if (!in_array($client_redirect_uri, $allowed_redirect_uris, true)) {
// 一致しない場合は不正なリクエストとして処理を中断!
header("HTTP/1.1 400 Bad Request");
echo "エラー: 許可されていないリダイレクトURIです。";
exit;
}
// 認証処理の継続...
?>
たったこれだけのチェックですが、ここをサボるとシステム全体が危険にさらされます。「面倒くさがるな、完全一致!」と覚えておいてくださいね。
—
5. まとめ:一歩ずつ、安全なアプリケーションを作ろう!
今回は、OAuth 2.0/OIDCにおける認可コード横取り攻撃のメカニズムと、それを防ぐためのPKCE、そしてリダイレクトURIの厳格な検証について解説しました。
- PKCE: 盗まれても意味のない「秘密の合言葉」をセットにして安全性を高める。
- リダイレクトURIの検証: 引換券の届け先を「完全一致」で厳しくチェックし、変な場所に送らせない。
セキュリティの世界は覚えることが多くて最初は圧倒されてしまうかもしれませんが、一つひとつの仕組みは私たちの現実世界の防犯と同じです。「どうやって泥棒を防ぐか?」という視点を持てば、コードを書くのがもっと楽しく、そして誇らしいものになりますよ。
一歩ずつ、安全で信頼されるエンジニアへの階段を登っていきましょう!次回もお楽しみに!
コメント