utsuroi xss labs 知識ベース 02 / 02 — XSS の3分類
XSS Types · Knowledge Base
02

XSS の3分類

Reflected, Stored, DOM-based — same root, different reach

XSS は3つのタイプに分類されます。攻撃の原理は「ブラウザに意図しないスクリプトを実行させる」で共通ですが、攻撃の到達経路と被害範囲が異なります。

1. Reflected XSS #reflected

「リクエストに含まれた攻撃文字列が、すぐ応答に反映される」型。

最も古典的な XSS。攻撃者が用意した URL を被害者がクリックすると、その URL に含まれるペイロードが応答 HTML に埋め込まれて返り、被害者のブラウザで実行されます。

典型的な攻撃フロー:

  1. 攻撃者が脆弱な URL を作る:
    https://example.com/search?q=<script>alert(1)</script>
  2. 攻撃者が SNS、メール、フィッシングサイト経由で被害者にこの URL を踏ませる
  3. 被害者のブラウザがその URL を開くと、サーバが q パラメータをそのまま HTML に埋め込んで返す
  4. 被害者のブラウザが <script> を実行

Reflected XSS の特徴は「永続化されない」こと。攻撃者は被害者ごとに URL を踏ませる必要があります。

2. Stored XSS #stored

「DBに保存された攻撃文字列が、見るユーザ全員のブラウザで実行される」型。

Reflected より被害が深刻なタイプ。攻撃者が一度ペイロードをサイトに保存すれば(投稿、コメント、レビュー、プロフィール等)、そのページを見る他のユーザ全員のブラウザで自動的に実行されます。

典型例:

Stored XSS は被害者を「だます」必要がない。普通にサイトを利用するだけで攻撃が発動します。SNS のような大規模サイトでは、1つの Stored XSS で何百万ユーザの Cookie が抜かれるリスクがある「ワーム化」も実例があります(2005年 Samy worm 等)。

3. DOM-based XSS #dom-based

「サーバを経由せず、ブラウザ内で完結する」型。

クライアント JavaScript がユーザ入力を取得して、それを innerHTMLdocument.write 等で DOM に注入することで発生する XSS。サーバ側のレスポンスには攻撃文字列が含まれません

典型例:

// クライアント JavaScript の脆弱な実装
const search = location.hash.substring(1);  // # 以降を取得
document.getElementById('result').innerHTML = '検索:' + search;

URL の #(フラグメント)以降はサーバには送信されない仕様。攻撃者が https://example.com/#<img src=x onerror=alert(1)> を被害者に踏ませると、サーバには気づかれずに XSS が成立する。

DOM-based の検出は難しい

サーバログには攻撃が一切残らないため、運用側が異変に気づきにくいのが DOM-based XSS の特徴。WAF も基本的にサーバ通信を監視するので、フラグメント経由の攻撃は素通りします。

4. 3つの比較 #comparison

どこに攻撃文字列が存在するか、で分類される。
Reflected Stored DOM-based
攻撃文字列の場所 URL/フォーム サーバ DB URL フラグメント等
持続性 なし(URL 単位) あり(永続) なし(URL 単位)
被害範囲 URL を踏んだ人だけ 該当ページ訪問者全員 URL を踏んだ人だけ
サーバログ 残る 残る 残らない場合あり
必要な仕掛け フィッシング等 投稿機能の悪用 フィッシング等

5. XSS の真の被害 #why-dangerous

alert(1) はあくまで証明であって、目的ではない。

XSS の練習問題でよく alert(1)alert(document.domain) を実行させますが、これはあくまで「JavaScript が実行できた」ことを示す目印。本当の攻撃ではもっと深刻な操作が行われます。

これらは Same-Origin Policy の制約下でも実行可能なので、XSS が成立した時点で「攻撃者がそのサイト上で被害者と同じ権限を持つ」と考えるべき。