レガシーの亡霊:JSONPという「聖域」に潜むXSSの深淵
現代のWebセキュリティにおいて、JSONP(JSON with Padding)は既に「化石」のような技術だ。CORS(Cross-Origin Resource Sharing)が標準化した今、あえてJSONPを採用する理由は皆無に等しい。しかし、現場のコードベースを監査していると、いまだに数世代前のレガシーAPIが、動的なデータロードの要として鎮座している光景を目の当たりにする。
JSONPは、本来ブラウザの「同一生成元ポリシー(SOP)」を回避するためのハックだった。タグの読み込みにはSOPが適用されないという仕様を悪用し、外部サーバーから渡されたデータ(JSON)を、クライアント側で定義したコールバック関数に「引数」として流し込む。この「流し込む」という行為そのものに、我々が最も警戒すべきインジェクションの脆弱性が隠されている。
1. メカニズムの分解:なぜJSONPは「実行」を許してしまうのか
JSONPのレスポンスは、単なるデータではなく「実行可能なJavaScriptコード」である。例えば、攻撃者が以下のようなリクエストを構築したとする。
GET /api/user?callback=alert(document.cookie)//
サーバー側の不適切な実装がこれを受け取り、コールバック関数名をバリデーションなしにレスポンスへ埋め込むと、レスポンスボディは以下のようになる。
/ サーバー側が生成した危険なレスポンス /
alert(document.cookie)//({"id": 123, "name": "Admin"});
ブラウザはこのレスポンスを「スクリプト」として解釈する。結果として、//以降のJSONオブジェクトはコメントアウトされ、攻撃者の意図したalert関数が実行される。これは古典的なXSSだが、現代のアーキテクチャでは、これが「認証済みのセッション」や「機密性の高いトークン」を盗み出すトリガーとなる。
2. 「防御」のレイヤを再定義する
多くの開発者は、コールバック名に対して「英数字のみを許可する」というブラックリスト方式で対処しようとするが、それは泥縄式だ。セキュリティアーキテクトの視点からは、以下の多層的な防衛ロジックを設計すべきである。
A. Content-Typeの厳格化とMIME Sniffingの無効化
サーバー側で Content-Type: application/javascript を強制し、かつ X-Content-Type-Options: nosniff ヘッダーを確実に送信すること。これにより、ブラウザの甘い推論を許さない。
B. コンテンツセキュリティポリシー (CSP) の動的適用
JSONPエンドポイントが属するオリジンに対して、厳格なCSPを適用する。特に script-src ディレクティブにおいて、信頼できるソース以外からの実行を禁止することは、JSONPインジェクションの最終的な防波堤となる。
/ CSPによる防護例 /
Content-Security-Policy: default-src 'self'; script-src 'self' https://trusted.api.com;
3. 実践:セキュアなコールバック関数名の検証ロジック
もしどうしてもJSONPを廃止できない場合、コールバック関数名のバリデーションは「ホワイトリスト」かつ「厳格な文字制限」で行う必要がある。
/
- セキュアなコールバック検証の例
- @param {string} callbackName - クライアントから渡されたコールバック名
- @returns {string} - 検証済み関数名またはデフォルト値
/
function validateCallback(callbackName) {
// 1. 文字列長を制限 (極端に長い名前はバッファオーバーフローのトリガーになり得る)
if (!callbackName || callbackName.length > 32) return 'callback';
// 2. 正規表現による厳格なホワイトリスト制御
// アルファベット、数字、ドットのみを許可。括弧やセミコロンは一切排除
const validCallbackRegex = /^[a-zA-Z0-9.]+$/;
if (!validCallbackRegex.test(callbackName)) {
throw new Error("Invalid callback name");
}
return callbackName;
}
4. 次世代への提言:AI時代における「ガードレイル」設計
今後、生成AIを組み込んだアプリケーションがAPIと直接対話するようになると、JSONPのような「意図しないコード実行」のリスクは、プロンプトインジェクションと融合してより複雑化する。
LLMがAPIレスポンスを解釈する際、JSONPのような構造が混入していれば、モデル自体が「スクリプト」をコードとして実行してしまう可能性がある。これを防ぐためには、APIのレスポンスそのものをJSON Schemaで厳格にバリデーションし、「実行可能なコンテキスト(関数呼び出し等)をレスポンスに含めない」という原則を徹底することだ。
最後に:セキュリティは「構造」に宿る
JSONPに脆弱性があるのは、それが「データ」と「実行」の境界線を曖昧にしているからだ。我々アーキテクトがやるべきことは、パッチを当てることではなく、この「境界の曖昧さ」を排除することである。
今すぐCORSへの移行を検討せよ。それができないのであれば、APIゲートウェイレベルでJSONPリクエストを検知し、強制的にクリーンなJSONへと変換(トランスコード)するプロキシ層を構築せよ。
脆弱性を探すのではない。脆弱性が入り込む余地のない「構造」を設計する。それが、最高峰のホワイトハッカーたる者の矜持である。
コメント