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
に指定したエンドポイントから機密情報を取得できます。
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);
}
}
JSONハイジャック攻撃は、CSRF(クロスサイトリクエストフォージェリ)トークンを使用することで防ぐことができます。リクエストに有効なトークンが含まれていない場合、サーバーはレスポンスを返さないようにします。
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の設定ミスや脆弱性を利用して、攻撃者が異なるオリジンからのリクエストに対して、許可されるべきでないデータを取得する攻撃です。
概要:
例:
malicious.com
)が、被害者のサイト(example.com
)に対してAJAXリクエストを送信し、example.com
からのレスポンスを取得できる場合、これはCORSの設定ミスが原因である可能性があります。CORSの問題が原因でJSONが取得されるシナリオ:
Access-Control-Allow-Origin: *
や、特定のオリジン(攻撃者のオリジンを含む)に対して許可を与えている場合。Access-Control-Allow-Credentials: true
を設定し、さらに不適切なオリジンを許可している場合、クッキーや認証情報が含まれるリクエストが攻撃者のオリジンから送信され、レスポンスが攻撃者に返されることがあります。概要:
関係性:
<script>
タグを使ったリクエストを利用し、JSONレスポンスがJavaScriptコードとして評価されることを悪用します。ただし、CORS設定が適切でない場合に、攻撃者がJSONデータを取得する手段を得ることがあるため、結果としてJSONハイジャックと類似した状況を生み出す可能性があります。
CORSの設定ミスでJSONデータが取得されるケースとJSONハイジャックは、いずれもブラウザのオリジンポリシーを回避してデータを盗む攻撃手法ですが、技術的には異なるアプローチです。どちらの問題も、適切なセキュリティ対策を講じることで防ぐことができます。JSONハイジャックの場合は、JSONレスポンスの構造を工夫することや、CORS問題を防ぐためには適切なCORS設定を行うことが重要です。