eval()を使ったJSONパース処理でのXSS攻撃

XSS

概要

eval()を使ってJSONをパースする処理において、巧妙なペイロードによりXSS攻撃が可能になる脆弱性について解説します。

前提

JSONデータを作成する場合エスケープすべき文字があります。
今回のケースでは\ バックスラッシュがエスケープされていません。

"  → \"     // ダブルクォート(文字列終了を防ぐ)
\  → \\     // バックスラッシュ(エスケープ文字)

\b → \\b     // バックスペース u+0008
\f → \\f     // フォームフィード u+000C
\n → \\n     // 改行文字 u+000A
\t → \\t     // タブ文字  u+000D
\r → \\r     // 復帰文字  u+0009

u+0000 ~ u+001F → \uXXXX

脆弱なコード例

// 危険なコード例
eval('var searchResultsObj = ' + this.responseText);

攻撃の仕組み

基本的な攻撃ペイロード
○入力値

\"-alert(1)}//

○生成されるJSONレスポンス

{"results":[],"searchTerm":"\\"-alert(1)}//"}

※”はエスケープされ\”となる
※そしてeval()ではjavascriptとして評価されるため\”ではなくその前の\\と”として評価される

○eval()で実行される実際のJavaScriptコード

eval('var searchResultsObj = ' + '{"results":[],"searchTerm":"\\"-alert(1)}//"}')

構文解釈の流れ

1.\\ は javascriptでは\となる
2.”searchTerm”:”\”で文字列が終了
2.-alert(1) がJavaScript演算式として実行される
3.alert(1)が副作用として実行される(ダイアログ表示)
4.”” – undefinedの演算結果はNaN
5.} でオブジェクトが終了
6.// で残りの文字列がコメントアウトされ構文エラーを回避

重要なポイント

JSON vs JavaScript の違い

要素JSON仕様JavaScript(eval)
“-alert(1)❌ 構文エラー✅ 有効な演算子
// コメント❌ サポートなし✅ 有効なコメント
関数呼び出し❌ 不可能✅ 実行される

修正方法

解決策としてeval()ではなくJSON.parse()を使う。

// 危険
eval('var searchResultsObj = ' + this.responseText);

// 安全
const searchResultsObj = JSON.parse(this.responseText);

サーバー側の問題

前提にも記載したように今回はサーバー側のエスケープ処理でバックスラッシュがエスケープされていませんでした。

このケースにおいてはクライアント側とサーバー側のどちらか一方を正しく修正すれば攻撃は防げますが、両方修正することで多層防御が実現できます。

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