このたび、GMO Flatt Security 開発者応援プログラム for バイブコーダー に選定いただき、 セキュリティ学習サービス 「KENRO」 を1か月無料で利用できることになりました!
「KENRO」は、実践的な脆弱性診断やセキュリティ演習を通じて、 開発者が 攻撃者の視点 から安全なシステム設計を学べるオンライン学習プラットフォームです。
本記事では、KENROの学習内容をもとに Cross-Site Scripting(XSS)脆弱性 についてまとめます。
Same-Origin Policy(SOP) の理解
XSS を正しく理解するには、まずブラウザの基本的な保護機構である Same-Origin Policy(SOP) を理解する必要があります。
-
Origin は
(スキーム, ホスト名, ポート番号)の組で定義されます(例:https://example.com:443) -
SOP の要点:
- 同一 Origin 間では自由に読み書きが可能。
- 異なる Origin 間では「読み込み」は原則禁止。「書き込み」も部分的に制限される。
もし SOP がなければ、攻撃者ページの JavaScript がユーザの機密ページを読み取り、外部に送信してしまう恐れがあります。Fetch や iframe、<img> のピクセル読み出しなどが対象です。
XSS(Cross-Site Scripting)とは
XSS = 攻撃者がウェブページ内に任意の HTML / JavaScript を注入し、それを被害者のブラウザで実行させる攻撃 です。SOP はクロスオリジン保護を提供しますが、同一オリジン内で攻撃者スクリプトが実行されると SOP は無力です。
典型的な流れ(概念)
- 脆弱なアプリがリクエストのパラメータを検証・エスケープせずに出力する
- 攻撃者が悪意あるスクリプトをパラメータとして埋め込み、被害者がその URL を開く
- 被害者のブラウザ上で攻撃者のスクリプトが
document.cookieや内部 API を操作する - 情報漏洩や偽のフォームを表示して入力情報を盗む(フィッシング)などの攻撃が成立する
XSS の分類
-
Reflected XSS(反射型)
- リクエストパラメータがそのままレスポンスに反映される
- 例:
?q=<script>...</script>をそのままechoしているケース
-
Stored XSS(蓄積型)
- 悪意ある入力がデータベース等に保存され、後続のページで表示される際に実行される
- 掲示板やコメント機能、ユーザプロフィールが典型
-
DOM-based XSS
- クライアントサイドの JavaScript が URL(
location.hashやlocation.search)や DOM の値をそのままinnerHTML等に書き込むことで発生 - サーバ側の出力は無関係で、クライアントのロジックに起因する
- クライアントサイドの JavaScript が URL(
XSS によるリスク(代表例)
-
セッションハイジャック:
document.cookieからセッション ID を盗まれるとログイン状態を乗っ取られる - 不正操作: 被害者のセッションを使って送金や設定変更などのリクエストを実行させられる
- フィッシング: 正規ドメイン上に偽ログインフォームを表示して機密情報(クレカ等)を盗む
対策(根本と補助)
1) コンテキストに応じた検証・エスケープ(根本対策)
-
HTML エスケープ: HTML 文脈(テキストノード)に出力するなら
<,>,&,",'などに変換する- 例(PHP):
htmlspecialchars($v, ENT_QUOTES, 'UTF-8') - 例(C#):
WebUtility.HtmlEncode(q)
- 例(PHP):
-
属性値: 属性値は必ずダブルクオートで囲い、
ENT_QUOTES相当でエスケープ -
URL 属性(href, src 等):
javascript:スキーム等の危険なスキームを検査・拒否するか、許可スキーム(http/https)に限定する -
テンプレートエンジン: 自動エスケープ機能を使う。ただし
|safeや@Html.Raw()等で無効化すると危険
2) DOM-based XSS の対策
-
innerHTML,document.write,evalを安易に使わない - テキストを挿入する場合は
innerText/textContent、要素を作る場合はcreateElement+appendChildを使う - どうしても HTML を扱うなら、DOMPurify のようなライブラリでサニタイズする
- Trusted Types の導入検討(ブラウザ対応状況に注意)
3) 二次対策(被害軽減)
-
Cookie に
HttpOnlyを付与し、JavaScript から参照できないようにする(セッション窃取対策) -
Content Security Policy(CSP) を設定して、許可済みスクリプトのみ実行できるようにする(
script-srcなど)。完全な防御ではないが被害を大幅に抑えられる - その他、
SameSite属性で一部の CSRF リスクを低減する
よくあるコード例(脆弱/修正例)
脆弱(PHP)
// 脆弱:GET パラメータをそのまま echo
echo $_GET['q'];
修正
// 安全:HTML エンコードして出力
echo htmlspecialchars($_GET['q'], ENT_QUOTES, 'UTF-8');
脆弱(JS, DOM-based)
<body></body>
<script>
const v = decodeURIComponent(location.hash.substring(1));
document.body.innerHTML = v; // 脆弱
</script>
修正
<body></body>
<script>
const v = decodeURIComponent(location.hash.substring(1));
document.body.innerText = v; // 安全
</script>
演習・学習リソース(備考)
(演習環境や課題がある場合はここにリンクや手順を記載してください。配布ファイルの修正や Docker を使った演習などは実務理解に有効です。)
まとめ
XSS は発見されやすくても放置されやすい脆弱性であり、被害が大きくなる可能性があります。入力の出力先(コンテキスト)に応じた適切なエスケープ と、DOM 操作を安全に行う習慣、そして HttpOnly / CSP といった二次対策の組み合わせが実務上は有効です。
まずはアプリケーション内の入力→出力フローを洗い出し、上記のチェックリストを回してみてください。