【実務・中級編】 JSONハイジャック(JSON Hijacking)の仕組みと対策 – オフェンシブセキュリティ & リバースエンジニアリング防御ガイド

JSONハイジャックの深淵:なぜ「ただの配列」が武器になるのか

現場のエンジニア諸君、お疲れ様。今日は、現代のWebアプリケーションにおいて「死角」となりがちなJSONハイジャックについて話そう。

「JSONなんて単なるデータ形式だし、HTMLとして解釈されるわけがないから安全だ」とタカをくくっていないか? その油断が、攻撃者に機密情報を引き渡す致命的な隙になる。JSONハイジャック(またはJSON Hijacking)は、ブラウザの仕様とJavaScriptの挙動を突いた、極めて古典的だが今なお強力な攻撃手法だ。

1. JSONハイジャックのメカニズム:配列の「魔力」

かつて、JSONP(JSON with Padding)という手法が流行した。クロスドメインでデータを取得するために、JSONをJavaScriptの関数呼び出しとしてラップして読み込む手法だ。JSONハイジャックは、この仕組みを悪用し、JSONを「JavaScriptのスクリプト」として読み込ませることで情報を奪う。

特に危険なのは、JSONが配列形式([...])で返される場合だ。

攻撃者は、ターゲットのWebサイト上のJSONエンドポイントに対して、自身の管理するサイトから <script src="..."></script> タグでリクエストを送る。もしそのJSONが配列形式であれば、JavaScriptのインタプリタはそれを「有効なスクリプト」として解釈しようとする。

攻撃者のPoC(概念実証)

攻撃者は、グローバルな Array コンストラクタを上書きすることで、データがパースされる瞬間にフックをかける。

// 攻撃者のサイトのスクリプト
// Arrayコンストラクタを乗っ取り、作成された瞬間にデータを盗む
const originalArray = Array;
window.Array = function() {
    console.log("盗んだデータ:", arguments);
    return new originalArray(...arguments);
};

// ターゲットのAPIを読み込む
// <script src="https://victim-site.com/api/user-data"></script>

ブラウザは通常、クロスドメインのスクリプト読み込みを許可している(CORSの制限とは別軸の話だ)。結果として、認証済みのユーザーのブラウザ上でこのスクリプトが実行され、機密データが攻撃者の元へ送信されてしまう。

—

2. 「防御の鉄則」:nosniff と CSRF対策

この攻撃を未然に防ぐために、現場で即座に導入すべき防衛策は2つある。

防御策A:X-Content-Type-Options: nosniff

これが最も手軽で強力な「バリア」だ。サーバーがレスポンスを返す際、ブラウザに対して「Content-Typeを勝手に推測(MIME Sniffing)するな」と命じる。

Nginxの設定例:

# nginx.conf または 各サイトの設定ファイル
# レスポンスヘッダーに強制付与する
add_header X-Content-Type-Options "nosniff" always;

これを設定しておけば、サーバーが application/json と宣言している限り、ブラウザはそれをHTMLやJSとして解釈することを拒否する。

防御策B:JSONを配列で返さない

根本的な設計思想として、JSONのトップレベルを配列([])にするのは避けるべきだ。常にオブジェクト({...})でラップする癖をつけよう。

NGなレスポンス:

[{"id": 1, "name": "Secret Data"}]

OKなレスポンス:

{"data": [{"id": 1, "name": "Secret Data"}]}

これだけで、JavaScriptの構文として実行しようとした際にエラーが発生し、配列のフックによるデータ流出を防ぐことができる。

—

3. 実践:セキュアな実装サンプル

では、PHPでAPIを構築する際の、教科書的な「セキュアな実装」を見てみよう。

<?php
// 1. レスポンスヘッダーの制御
// Content-Typeを厳密に指定
header('Content-Type: application/json; charset=utf-8');
// ブラウザの誤解釈を防ぐ
header('X-Content-Type-Options: nosniff');
// CSRF対策:カスタムヘッダーの検証(API利用時)
$token = $_SERVER['HTTP_X_CSRF_TOKEN'] ?? '';
if (!validate_csrf_token($token)) {
    http_response_code(403);
    exit('Forbidden');
}

// 2. データの生成(必ずオブジェクトでラップする)
$userData = [
    ['id' => 1, 'name' => 'John Doe'],
    ['id' => 2, 'name' => 'Jane Smith']
];

$response = [
    'status' => 'success',
    'data' => $userData // 配列を直接返さない
];

echo json_encode($response);

現場のエンジニアへ送る言葉

セキュリティとは、巨大な城壁を築くことではない。「ブラウザはこれくらいやってくれるだろう」という甘い期待を捨て、ブラウザの挙動をコントロールするヘッダーを一つ一つ丁寧に記述することだ。

  • X-Content-Type-Options: nosniff は全レスポンスに含めること。
  • APIのエンドポイントは、必ず認証(OAuth/JWT)とCSRF対策(カスタムヘッダーの要求)を組み合わせること。

今回のJSONハイジャックは、一見すると古い手口に見えるが、モダンなSPA(Single Page Application)でもAPIの設計次第ではいまだに通用する。君たちの手元にあるプロダクトが、明日、意図しない「スクリプト」として悪用されないよう、今すぐ設定を確認してほしい。

何かあればいつでも相談してくれ。堅牢なシステムを共に作ろう。

コメント

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