問い合わせ、検索結果、コメントは、普通のページ内容に見えます。 しかし、サイトがその内容をコードとして扱えば、攻撃の入口になります。 クロスサイトスクリプティング(XSS)は、この境界の誤りを突き、 攻撃者が用意した内容を、別の利用者が信頼しているページ内で実行させます。 影響はサイトの作り、利用者の権限、防御策によって変わります。
XSSはファイルをインストールしなくても被害を生みます。 注入されたコードは、ページに表示された情報を読んだり、表示を変えたり、 そのページからリクエストを送ったりできます。開発者は信頼できないデータを 実行可能な内容にしないこと、利用者はブラウザの隔離で防げる範囲を知ることが大切です。 ブラウザ内での動作は MDNのXSS解説 でも確認できます。
XSSの基本

XSSは、信頼できないデータがページ内で実行可能な内容として解釈されると起きます。 コードはそのページのオリジンで動き、DOMを操作し、ログイン中の利用者として サイトへリクエストを送れる場合があります。ただし、 同一オリジンポリシー は別のオリジンへのアクセスを制限します。HttpOnly属性のあるCookieはdocument.cookieから読めませんが、コードがログイン中のページ内で 操作することまで防ぐわけではありません。
CookieのSameSite属性は、主にサイトをまたぐリクエストでCookieを送る条件を制御し、 一部のCSRF攻撃を抑えるのに役立ちます。しかし、サイト自身のページ内で動く スクリプトは取り除きません。SameSiteとHttpOnlyの違いは MDNのSet-Cookie資料 にまとめられています。
会社の掲示板に例えてみましょう。訪問者はメモを貼れても、掲示板の指示や操作欄まで 書き換えられてはいけません。XSSでは、訪問者のメモが掲示板そのものの一部として 扱われます。データが実行可能な場所に入り込んだ時点で、境界が破られるのです。
主な3つの型と関連する手口

次の3分類は、信頼できないデータがどこから入り、どこで実行可能になるかを表します。 分類が重なることもあります。例えば、サーバーが返した値を、クライアント側のコードが 危険なDOM操作に渡す場合です。
保存型XSS。アプリケーションがコメント、プロフィール、問い合わせなどに攻撃者の内容を保存し、 後からほかの利用者に表示します。影響を受ける範囲は、その内容を誰が見られるか、 サイトがどう表示するかによって決まります。
反射型XSS。サーバーが、URLのパラメータなどリクエストに含まれる値を、表示先に合った処理をせずに 応答へ入れます。細工されたリンクから、その応答を開かせる手口があります。
DOMベースのXSS。クライアント側のコードが信頼できないデータを読み、innerHTMLのような 危険なDOM操作に渡します。データの出所はURL、サーバーの応答などさまざまで、 問題の核心はブラウザ内での処理です。詳しくは OWASPのDOMベースXSS対策 を参照してください。
Self-XSSは別の手口です。攻撃者が利用者自身に、開発者ツールへコードを貼るよう 誘導します。XS-Leaksは、オリジンをまたぐ観測可能な反応から情報を推測する別の 問題です。XSSの核心は、信頼できないデータが信頼されるページ内で動くことにあります。
XSS攻撃の流れをステップごとに
典型的な流れを追うと、アプリケーション側の対策で攻撃を止められる箇所が見えます。
- 1
入力の受け口を見つける
攻撃者は、コメント、URLのパラメータ、メッセージなど、 最終的にページへ表示される値を探します。 - 2
危険な出力先に届く
アプリケーションがその値を、単なる文字ではなくHTMLやスクリプトとして 解釈され得る場所へ入れてしまいます。 - 3
対象のページを開かせる
細工したリンクや共有された内容などを通じて、利用者を問題のページへ誘導します。 - 4
ページ内でコードが動く
実行されたコードはページと同じオリジンで動きます。ただし、利用できる内容やAPIは サイトとブラウザの制限を受けます。 - 5
可能な操作を悪用する
アプリケーション次第で、表示の変更、露出しているデータの読み取り、 現在のセッションでのリクエスト送信などが起こり得ます。

注入されたコードにできること

XSSの影響は見た目の改ざんだけではありません。ただし、何ができるかは そのページで扱うデータや操作によって変わります。次の4例は可能性を示すもので、 発生頻度の順位ではありません。
アカウント上の操作。注入されたコードは、ログイン中のページからリクエストを送り、JavaScriptに公開された トークンを読める場合があります。HttpOnlyはCookieの直接読み取りを防ぎますが、 同一オリジンでの操作まで止めません。詳しくは MDNのCookie解説 を参照してください。
認証情報の取得。書き換えた入力欄で、利用者が入力するパスワードなどを集める可能性があります。 利用者がそのページに入力する必要があり、別の場所に保存されたパスワードが XSSだけで自動的に見えるわけではありません。
ページ表示の改ざん。リンク、案内文、取引内容の表示を変えたり、サイトの制限内で別のウェブ資源を 読み込ませたりできます。端末上でマルウェアを動かすには、ブラウザの脆弱性を 突くか、利用者がファイルを実行するなど、別の段階が必要です。
データの流出。ページにすでに表示されている情報を読み、通信制限が許す範囲で外へ送れる場合があります。 同一オリジンポリシーは無関係なサイトの読み取りを制限します。主な問題は、 脆弱なサイト自身のデータと権限です。
5つの想定例
以下は実際の事件ではなく、想定例です。それぞれ、データがコードになる境界をどこで確認すべきか示します。
交流サイト。コメントのプレビューが投稿されたHTMLを実行可能な形で表示します。 ログイン中の管理者が開くと、注入されたコードが管理画面で操作できるかもしれません。 安全な表示方法と、管理者向け機能の確認が必要です。
オンラインショップ。検索語が、表示場所に合った処理を経ずにHTMLの応答へ入ります。 細工されたURLを開いた買い物客には、書き換えられた決済先リンクが見えるかもしれません。 正規のドメインが表示されていても、そのリンクまで安全とは限りません。
患者向けポータル。保存されたメッセージが医師のブラウザに表示されます。CookieがHttpOnlyでも、 注入されたコードはページ内の情報を読んだり、医師に許されたリクエストを 送ったりできる可能性があります。アクセス制御と安全な表示の両方が重要です。
金融サービスの管理画面。クライアント側のコードがURLの一部をinnerHTMLに書き込みます。 悪意あるリンクで表示が変わるかもしれません。サーバー側の確認や承認が 不正な送金を防ぐ場合もありますが、DOMの欠陥は別に修正する必要があります。
公共情報サイト。古いCMSのテンプレートが、見出し欄のHTMLを信頼して表示します。 悪意ある編集者や乗っ取られたアカウントが案内を書き換えるかもしれません。 古いテンプレートと、編集権限の両方を見直す必要があります。
XSSがなくならない理由
新しいフレームワークは多くの値を自動的に安全な形で表示します。それでも、 生のHTML、危険なDOM操作、テンプレートの近道、外部コードによって防御を 迂回することがあります。OWASPはXSSを含むインジェクションをウェブアプリの リスクとして扱っています。 OWASPのXSS対策ガイド は、一つの対策ですべての場面を守れない理由を説明しています。
大規模なアプリケーションでは、古いテンプレート、新しい部品、利用者が入力した 装飾付きの文章、外部スクリプトが混在します。適切な処理は、データの入り口と 出力先によって異なります。HTML本文、属性、URL、スクリプト、DOM操作には それぞれ違う規則が必要です。自動検査は役立ちますが、サーバー応答だけの検査では クライアント側の変換を見落とす場合があります。ブラウザでの試験とコードレビューも 組み合わせてください。
利用者ができる対策
利用者が脆弱なサイトそのものを修正することはできません。それでも、 そのサイトに見せる情報を減らすことはできます。次の対策はリスクを下げますが、 XSSからの完全な保護を約束するものではありません。
認証情報の入力や重要な操作の承認前に、サイトのアドレスを確認してください。 ブラウザを更新し、不要な拡張機能を削除しましょう。信頼度の違う作業では アカウントやブラウザのプロファイルを分ける方法もあります。サイトの侵害が疑われるときは、 信頼できる端末からログアウトし、アカウントの利用履歴を確認してください。 他のセッションも無効になるかはサービス次第です。Browser.lolはウェブサイトのコードを 遠隔のブラウザで動かしますが、XSSはその中でログイン中のアカウントや入力・承認内容に 影響し得ます。表示画面、クリップボード、ダウンロードも端末との接点です。
開発者とセキュリティチーム向けチェックリスト
XSSを防ぐ責任はアプリケーション側にあります。次の5項目から始め、 実際に使っている表示箇所で検証してください。
出力先に合わせて処理する。HTML本文、属性、URL、JavaScript、CSSでは必要な処理が異なります。 普通の文章には文字列だけを挿入するAPIを使い、装飾付きHTMLが必要な場合は 保守されている適切な無害化ライブラリを使ってください。
フレームワークの保護を理解して使う。React、Vue、Angularは表示する値の安全な処理を助けますが、生のHTMLを扱う機能や DOMの直接操作では、その保護の一部を迂回します。例外的な処理を重点的に見直しましょう。
制限の厳しいCSPを設定する。nonceやハッシュを使う適切なコンテンツセキュリティポリシーは、注入後のスクリプト実行を 制限できます。ただし、出力の安全な処理に代わるものではありません。 OWASPのCSPガイド も参考にしてください。
データの流れと危険な操作を調べる。静的解析は、確認を経ないinnerHTMLへの代入などを見つける助けになります。 描画後のページや、クライアント側でデータが流れる経路を試す検査も加えましょう。
権限の高い操作を点検する。担当者が利用者の投稿を見る画面、装飾付き文章を許す画面、重要な操作を行える画面を 重点的に試してください。遠隔ブラウザでの試験は端末がウェブコードに触れる機会を 減らせますが、実証用コードを無害にはしません。試験用アカウントを使い、 本物の秘密情報は持ち込まないでください。
データを実行可能な場所に入れない

XSSは、アプリケーションがデータとコードの境界を越えてしまう問題です。 出力先に合った処理、安全なDOM操作、装飾付きHTMLの慎重な扱い、制限の厳しいCSPは、 信頼できないデータがコードになる可能性を下げます。Cookieの属性やサーバー側の確認は 影響の一部を抑えられますが、一つですべてを解決する対策はありません。
遠隔ブラウザはウェブサイトのコードが動く場所を変えますが、脆弱なサイトを修正したり、 遠隔セッション内のアカウント操作を無害にしたりはしません。重要なアカウントを分け、 影響の大きい操作を確認し、データがコードになる原因をサイト側で直してください。 それぞれ違う部分のリスクに対処するため、組み合わせることが大切です。



