【実務・中級編】JSONPの脆弱性とCORS(Cross-Origin Resource Sharing)への移行 – アプリケーションセキュリティ & 安全な開発防御ガイド

レガシーの亡霊「JSONP」を葬り去れ:CORSで実現する現代的なクロスオリジン通信の鉄則

現場でコードレビューをしていると、未だに「古いシステムとの連携だから」という言い訳でJSONPを使っているケースに出くわす。正直に言おう。JSONPを使い続けることは、自ら鍵を開けたまま外出するようなものだ。

今回は、XSSの温床であるJSONPをなぜ今すぐ捨て、CORS(Cross-Origin Resource Sharing)へ移行すべきなのか。その技術的必然性と、今日から使える実装の「正解」を叩き込む。

—

1. なぜJSONPは「設計上の欠陥」なのか

JSONP(JSON with Padding)の仕組みは単純だ。

攻撃者がこのcallbackパラメータを操作すれば、あなたのサイトのコンテキストで任意のJavaScriptを実行できる。これが反射型XSSの完成だ。さらに厄介なのは、JSONPはCORS以前の時代には「唯一の回避策」であったため、セキュリティ境界が曖昧なまま運用されているケースが多いことだ。

攻撃シナリオ:PoCの恐怖

攻撃者は、ターゲットのユーザーを罠サイトへ誘導し、セッションCookieを盗み出すスクリプトをJSONPのコールバック経由で流し込む。現代のWebアプリケーションにおいて、これは単なる「警告」ではなく、アカウント乗っ取りという最悪の結末を意味する。

---

2. CORS:ブラウザによる「信頼のゲートキーパー」

CORSは、JSONPのようにスクリプトを「注入」するのではなく、サーバー側が「誰からのリクエストを許可するか」をHTTPヘッダーで制御する仕組みだ。ブラウザが仲裁し、許可されたオリジン以外からの読み取りをブロックする。

実装の鉄則:ワイルドカードは禁忌

「とりあえず動けばいい」と Access-Control-Allow-Origin: を設定するのは厳禁だ。認証情報(CookieやAuthorizationヘッダー)を伴う通信では機能しないし、セキュリティリスクも大きい。

【サーバー側の実装例:PHP】

適切なホワイトリスト管理が鍵となる。

---

3. インフラレイヤーでの守り:Nginx設定

アプリケーションコードで制御できない場合や、フロントエンドの疎通確認をより厳格にしたい場合は、Nginxレベルで制御するのも有効だ。

Nginxの設定例
location /api/ {
# オリジンの検証ロジックをマップする
set $cors_origin "";
if ($http_origin ~ "^https?://(app\.example\.com|partner\.example\.com)$") {
set $cors_origin $http_origin;
}

add_header 'Access-Control-Allow-Origin' $cors_origin always;
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always;
add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization' always;

# プリフライト対策
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' $cors_origin;
add_header 'Access-Control-Max-Age' 3600;
return 204;
}
}

---

4. 最後に:セキュリティは「信頼の積み重ね」

JSONPからCORSへの移行は、単なる技術的な書き換えではない。「外部からの入力をそのまま実行する」という古い悪癖を捨て、「厳格なオリジン検証」という現代の標準に乗り換えるという決断だ。

現場のエンジニアへ:
「動いているからいい」は脆弱性の入り口だ。JSONPのコードを見つけたら、それは技術的負債ではなく「時限爆弾」だと認識してほしい。

1. JSONPエンドポイントを特定する
2. 通信元のオリジンを洗い出す
3. CORSヘッダーを適切に設定し、JSONPを無効化する

この手順を徹底するだけで、あなたのプロダクトは格段に硬くなる。セキュリティは魔法ではなく、こうした泥臭い確認と適切な設定の積み重ねでしかないのだから。さあ、今すぐコードベースのクリーンアップに取り掛かろう。

コメント

タイトルとURLをコピーしました