WebVulnerabilities

JSONハイジャックについて

概要

JSONハイジャック (JSON Hijacking) とは、攻撃者が他人のセッション内で実行されるJavaScriptを利用し、JSON形式で送信される機密情報を盗み出す攻撃手法です。この攻撃は主に、ウェブアプリケーションがユーザーの機密データをJSON形式で返し、それをブラウザで直接評価する場合に発生します。

特に、ブラウザが同一オリジンポリシーを厳格に適用しない場合や、古いブラウザが使われている場合に、この攻撃が成立する可能性が高まります。

脆弱性を持つコードの例と攻撃手法

言語に依存しない一般的な問題として、JSONハイジャックは以下のような状況で発生します。

脆弱性を持つコードの例:

仮に以下のようなAPIエンドポイントがあったとします。

<?php
header('Content-Type: application/json');
$data = array('username' => 'john_doe', 'email' => 'john@example.com');
echo json_encode($data);
from flask import Flask, jsonify

app = Flask(__name__)

@app.route('/user')
def user():
    data = {'username': 'john_doe', 'email': 'john@example.com'}
    return jsonify(data)
const express = require('express');
const app = express();

app.get('/user', (req, res) => {
    res.json({ username: 'john_doe', email: 'john@example.com' });
});

app.listen(3000);
import javax.servlet.http.*;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;

public class UserServlet extends HttpServlet {
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
        response.setContentType("application/json");
        ObjectMapper mapper = new ObjectMapper();
        User user = new User("john_doe", "john@example.com");
        mapper.writeValue(response.getOutputStream(), user);
    }
}

class User {
    public String username;
    public String email;

    public User(String username, String email) {
        this.username = username;
        this.email = email;
    }
}

攻撃手法:

攻撃者が次のような悪意のあるページを用意します:

<!DOCTYPE html>
<html>
<head>
    <title>JSON Hijacking</title>
</head>
<body>
    <script>
        window.onload = function() {
            var script = document.createElement('script');
            script.src = 'https://example.com/user'; // これは脆弱なAPIエンドポイントです
            document.body.appendChild(script);
        }
    </script>
</body>
</html>

このページが被害者のブラウザで開かれると、APIエンドポイントからJSONデータが読み込まれ、scriptタグとして評価されます。JSONデータがJavaScriptのコードとして実行されるため、攻撃者はscript.srcに指定したエンドポイントから機密情報を取得できます。

防止策

1. JSONラッピング

JSONレスポンスを配列ではなくオブジェクトにラップする方法です。JavaScriptは配列のJSONをそのまま評価してしまいますが、オブジェクトを直接評価することはできません。

例:

<?php
header('Content-Type: application/json');
$data = array('username' => 'john_doe', 'email' => 'john@example.com');
echo 'while(1);' . json_encode($data);
from flask import Flask, jsonify

app = Flask(__name__)

@app.route('/user')
def user():
    data = {'username': 'john_doe', 'email': 'john@example.com'}
    return 'while(1);' + jsonify(data).get_data(as_text=True)
const express = require('express');
const app = express();

app.get('/user', (req, res) => {
    res.set('Content-Type', 'application/json');
    res.send('while(1);' + JSON.stringify({ username: 'john_doe', email: 'john@example.com' }));
});

app.listen(3000);
import javax.servlet.http.*;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;

public class UserServlet extends HttpServlet {
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
        response.setContentType("application/json");
        response.getOutputStream().print("while(1);");
        ObjectMapper mapper = new ObjectMapper();
        User user = new User("john_doe", "john@example.com");
        mapper.writeValue(response.getOutputStream(), user);
    }
}

2. CSRFトークンの使用

JSONハイジャック攻撃は、CSRF(クロスサイトリクエストフォージェリ)トークンを使用することで防ぐことができます。リクエストに有効なトークンが含まれていない場合、サーバーはレスポンスを返さないようにします。

3. HTTPヘッダーを使った防御

X-Content-Type-Options: nosniff ヘッダーを使用し、ブラウザがスクリプトとして誤ってコンテンツを解釈しないようにします。また、APIエンドポイントでContent-Disposition: attachment ヘッダーを設定し、コンテンツがファイルとして扱われるようにすることも有効です。

例:

<?php
header('Content-Type: application/json');
header('X-Content-Type-Options: nosniff');
$data = array('username' => 'john_doe', 'email' => 'john@example.com');
echo json_encode($data);
from flask import Flask, jsonify, make_response

app = Flask(__name__)

@app.route('/user')
def user():
    data = {'username': 'john_doe', 'email': 'john@example.com'}
    response = make_response(jsonify(data))
    response.headers['X-Content-Type-Options'] = 'nosniff'
    return response
const express = require('express');
const app = express();

app.get('/user', (req, res) => {
    res.set('Content-Type', 'application/json');
    res.set('X-Content-Type-Options', 'nosniff');
    res.json({ username: 'john_doe', email: 'john@example.com' });
});

app.listen(3000);
import javax.servlet.http.*;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;

public class UserServlet extends HttpServlet {
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
        response.setContentType("application/json");
        response.setHeader("X-Content-Type-Options", "nosniff");
        ObjectMapper mapper = new ObjectMapper();
        User user = new User("john_doe", "john@example.com");
        mapper.writeValue(response.getOutputStream(), user);
    }
}

まとめ

JSONハイジャックは、特に古いブラウザや不適切な実装において、攻撃者がJSONデータを盗み出すことが可能になる脆弱性です。主な防止策には、JSONレスポンスのラッピング、CSRFトークンの導入、適切なHTTPヘッダーの使用などがあります。これらの対策を実装することで、JSONハイジャックのリスクを効果的に軽減することが可能です。

CORS(Cross-Origin Resource Sharing)の問題を利用してJSONデータが取得できる手法とJSONハイジャックは関連する部分があるものの、技術的には異なる攻撃手法です。以下にそれぞれの違いと関係性について説明します。

CORSの問題によるJSONデータの取得

CORSの設定ミスや脆弱性を利用して、攻撃者が異なるオリジンからのリクエストに対して、許可されるべきでないデータを取得する攻撃です。

概要:

例:

CORSの問題が原因でJSONが取得されるシナリオ:

JSONハイジャック

概要:

関係性:

違いと関係性

ただし、CORS設定が適切でない場合に、攻撃者がJSONデータを取得する手段を得ることがあるため、結果としてJSONハイジャックと類似した状況を生み出す可能性があります。

まとめ

CORSの設定ミスでJSONデータが取得されるケースとJSONハイジャックは、いずれもブラウザのオリジンポリシーを回避してデータを盗む攻撃手法ですが、技術的には異なるアプローチです。どちらの問題も、適切なセキュリティ対策を講じることで防ぐことができます。JSONハイジャックの場合は、JSONレスポンスの構造を工夫することや、CORS問題を防ぐためには適切なCORS設定を行うことが重要です。