レガシーの亡霊を葬る:JSONPという「終わった」技術が招くXSSの深淵
「JSONPはもう死んだ」――そう信じているアーキテクトは多い。しかし、レガシーシステムの墓場を掘り返せば、今なお生き残るJSONPの残骸が、脆弱性の温床として蠢いている。現代のSPA(Single Page Application)全盛期において、なぜ今さらJSONPなのか。それは、クロスドメイン制約を回避するための「古い呪文」が、現代のブラウザセキュリティモデルにおいて極めて危険なバックドアになり得るからだ。
今日は、JSONPにおけるコールバック関数の検証不備が、単なる「XSS」の枠を超え、どのようにしてブラウザのセキュリティポリシーを無力化するのか、その低レイヤのメカニズムを解剖する。
—
1. JSONPの構造的欠陥と「信頼」の誤謬
JSONP(JSON with Padding)の基本原理は、タグが持つ「外部ドメインのスクリプトを読み込める」という仕様を悪用した、クロスドメイン通信のハックだ。
// クライアント側
// APIは ?callback=handleResponse というクエリを投げ、
// サーバーは handleResponse({...}) という形のレスポンスを返す。
ここでの根本的な脆弱性は、サーバー側が「コールバック関数名(callback パラメータ)」を、単なる識別子ではなく「実行可能なコードの一部」として盲信している点にある。
攻撃者は callback=alert(document.cookie) といった文字列を注入する。サーバーがこれをサニタイズせずそのままレスポンスボディに埋め込むと、ブラウザはそれを「サーバーが許可した正当な関数」と解釈し、実行する。これがJSONP-XSSの正体だ。
2. 攻撃者の視点:なぜ「Content-Type」は防波堤にならないのか
「JSONPでも、Content-Type: application/json を返せば安全ではないか?」という議論が過去にあった。しかし、これは危険な幻想だ。
ブラウザのMIMEスニッフィング機能は、たとえレスポンスヘッダーが application/json であっても、ボディの先頭がHTMLタグやスクリプトとして解釈可能な場合、あるいはContent-Typeが text/html にフォールバックされるようなエッジケースにおいて、強制的に実行を開始することがある。
特に、CSP(Content Security Policy)を導入していても、JSONPエンドポイントが許可されている場合、攻撃者はそのエンドポイントを「信頼されたソース」として悪用することが可能だ。これは「信頼されたドメインから送られてくるスクリプト」という形式をとるため、単純なCSPでは防ぎきれないケースが多い。
---
3. 鉄壁の防衛:アーキテクトが取るべき実装指針
JSONPを完全に廃止し、CORS(Cross-Origin Resource Sharing)へ移行するのが唯一の解だが、どうしても残さざるを得ないレガシー資産がある場合は、以下の防御アーキテクチャを適用せよ。
厳格なコールバック名のホワイトリスト検証
コールバック関数名は、許可された文字セット(英数字およびアンダースコア)のみで構成されるべきだ。
import re
def validate_callback(callback_name):
# 許可する文字パターン: 英数字とアンダースコアのみ
# JSの関数名として妥当な範囲に制限する
pattern = re.compile(r'^[a-zA-Z0-9_]+$')
if not callback_name or not pattern.match(callback_name):
# 不正な場合は即座にリクエストを拒否する
raise ValueError("Invalid callback name")
return callback_name
コンテンツタイプとレスポンスの分離
もし、JSONPがどうしても必要なら、以下の防御層を追加する。
1. X-Content-Type-Options: nosniff を必ず付与する(MIMEスニッフィングの抑制)。
2. Content-Type: application/javascript; charset=utf-8 を指定し、JSONPであるという意図を明確にする。
3. セキュリティヘッダーの付与: Content-Security-Policy: default-src 'self' を設定し、信頼できない外部スクリプトの実行をブラウザ側で禁止する。
---
4. セキュリティアーキテクトへの問い:生成AIと次の脆弱性
今後、生成AIがコードを生成する時代において、JSONPのような「古い仕様」をAIがコードベースに含めてしまうリスクがある。AIは「効率的な通信手段」として過去のスタックを提案するが、そこに潜むセキュリティリスクをAIが完全に文脈理解しているとは限らない。
我々専門家に求められるのは、「コードが動くこと」ではなく「コンテキストにおいてそのコードがどのような脆弱性を内包するか」を読み解く能力だ。
今後の監査の観点
- 静的解析(SAST)の強化: JSONPに関連するパラメータ(
callback,jsonp等)が、入力値検証を経由せずにレスポンスに直接書き込まれていないかをルール化する。 - 動的解析(DAST): ペイロードとして関数呼び出しを含めたリクエストを投げ、レスポンスが「実行可能」かどうかを判定するファジングをパイプラインに組み込む。
JSONPは、もはや技術的負債の代名詞だ。この脆弱性を放置することは、強固な城壁に自ら通用口を掘るようなもの。一刻も早くCORSへの移行ロードマップを策定し、レガシーの呪縛からインフラを解放せよ。それが、システムを守る唯一の道である。
コメント