JSONPという「レガシーの呪縛」:コールバック注入が招く境界防御の崩壊
現代のWebアーキテクチャにおいて、JSONP(JSON with Padding)は既に「負の遺産」として扱われるべき存在だ。しかし、レガシーなサードパーティAPIや、古いモバイルアプリとの互換性を維持するために、今なお死に体で運用されているエンドポイントは少なくない。
セキュリティアーキテクトとして断言するが、JSONPを放置することは、自ら「ブラウザの同一生成元ポリシー(SOP)を無効化するバックドア」を維持しているに等しい。本稿では、なぜJSONPがXSSの温床となり、それをいかにして現代的なCORSアーキテクチャへと昇華させるべきか、その本質を紐解く。
—
1. なぜJSONPはXSSの特等席なのか
JSONPの仕組みは極めてシンプルだ。クライアントがURLパラメータに callback=handleResponse と指定すれば、サーバーはレスポンスを handleResponse({...data...}) というJavaScriptの関数呼び出しとしてラップして返す。
ここでの致命的な脆弱性は、「コールバック関数名のバリデーション不備」にある。攻撃者はここを突き、悪意のあるスクリプトを注入する。
攻撃シナリオ:コールバック注入
例えば、以下のようなURLを想像してほしい。
https://api.victim.com/data?callback=
サーバー側がこの callback パラメータを正規表現等で厳格にチェックせず、そのままレスポンスに埋め込んだ場合、ブラウザはそれを「信頼されたサーバーからの動的スクリプト」と誤認し、即座に実行する。これは反射型XSSの亜種だが、SOPをすり抜けるという点で非常にタチが悪い。
// サーバーサイドの脆弱な実装例 (Node.js/Express)
app.get(‘/api/data’, (req, res) => {
const cb = req.query.callback; // ここが地獄の入り口
const data = { user: ‘admin’, secret: ‘… ‘ };
// 攻撃者が callback に を仕込むと…
// Content-Type: application/javascript として実行されてしまう
res.send(${cb}(${JSON.stringify(data)}));
});
—
2. 根本原因:コンテキストの混同とMIMEタイプの脆弱性
ブラウザがなぜこの攻撃を許すのか。それは、 タグによるリクエストが、CORSの制約を受けないという仕様上の「仕様」に依存しているからだ。
1. スクリプトの実行コンテキスト: JSONPは外部スクリプトとして読み込まれるため、HTMLコンテキストではなくJavaScriptコンテキストで実行される。これにより、一部のWAFやブラウザのXSSフィルターを回避できる可能性が高まる。
2. MIMEタイプの盲点: サーバーが application/javascript を返すと、ブラウザは「これはコードである」と認識する。たとえ中身がJSONデータであっても、コールバック名が汚染されていれば、それは任意のコード実行となる。
---
3. 防衛アーキテクチャ:CORSへの完全移行
JSONPを駆逐し、CORS(Cross-Origin Resource Sharing)へと移行することは、単なる技術刷新ではない。「許可された境界の明示」というセキュリティの基本原則への回帰だ。
CORS実装のベストプラクティス
CORSへの移行には、サーバーサイドでの厳格なヘッダー制御が不可欠である。以下は、ホワイトリストに基づいた安全なCORS設定の例だ。
// セキュアなCORSポリシーの構築
const allowedOrigins = ['https://app.trusted-domain.com'];
app.use((req, res, next) => {
const origin = req.headers.origin;
if (allowedOrigins.includes(origin)) {
// 許可されたオリジンのみにアクセスを限定
res.setHeader('Access-Control-Allow-Origin', origin);
}
// 認証情報を含むリクエストの許可(必要に応じて)
res.setHeader('Access-Control-Allow-Credentials', 'true');
next();
});
---
4. チーフホワイトハッカーの視点:アーキテクチャの監査
もしあなたが大規模システムのアーキテクトであれば、以下の3点を監査リストに加えよ。
1. コールバックのホワイトリスト化: どうしてもJSONPを外せない場合、コールバック名に利用できる文字種を ^[a-zA-Z0-9_]+$ に限定し、許可リスト以外の文字が含まれた場合は即座に400 Bad Requestを返せ。
2. Content-Typeの強制: レスポンスヘッダーを application/json に固定し、ブラウザに「これはスクリプトではない」と強制的に認識させる。X-Content-Type-Options: nosniff ヘッダーは必須だ。
3. CSP(Content Security Policy)による封じ込め:
Content-Security-Policy: script-src 'self' https://trusted.api.com;
このようにCSPを設計することで、万が一XSSが混入しても、許可されていないドメインからのスクリプト実行をブラウザレベルで阻止できる。
---
結論:技術的負債を「セキュリティの債務」と捉えよ
JSONPの悪用は、単なるコードの書き方の問題ではない。「通信の文脈を無視したデータ共有」という設計思想そのものの限界だ。
現代のWebセキュリティにおいて、信頼は「なんとなく動く」ことではなく、「どこからの通信を、どの範囲で許可するか」という数学的な証明によって担保される。JSONPというレガシーをCORSというモダンなガードレールに置き換えることは、インフラの堅牢性を底上げする最もコストパフォーマンスの高い投資である。
次にコードをレビューする際、もし callback という文字列を見かけたら、それは単なるバグではなく、システムに空いた「窓」だと思って対処してほしい。我々の仕事は、その窓を閉め、強固な防衛壁を築くことにある。
コメント