XSS攻撃(クロスサイトスクリプティング)は、ウェブアプリの代表的な脆弱性です。本記事では、XSSの仕組みや代表的な種類(ストアド・リフレクト・DOM XSS)、攻撃の流れや実際の被害例、HTTPSとの関係、セッションハイジャックなどの危険性、加えて出力エスケープやCSPなど有効な対策方法まで詳しく解説します。ウェブ開発者や運営者必見の内容です。
XSS攻撃(クロスサイトスクリプティング)は、ウェブアプリケーションの脆弱性の一つであり、攻撃者が自分のJavaScriptコードを他のユーザーのブラウザ上で実行させることを可能にします。この脆弱性は、サーバー自体が正常に動作している場合でも発生し、アプリケーションがデータの処理を誤ることで、ブラウザがそのデータをページの一部や実行可能なコードとして認識してしまうことが原因です。
このXSSを利用することで、ウェブサイトの内容改ざん、偽のフォーム表示、ユーザーになりすました操作や、ページ上でアクセス可能な一部情報の窃取が可能となります。特に危険なのは、悪意あるコードが本物のサイト内部で実行されるため、そのページのJavaScriptと同等の権限を持つ点です。
クロスサイトスクリプティング(Cross Site Scripting, XSS)は、サイトが信頼できないユーザーデータを適切なエスケープや安全な処理をせずにHTMLページへ挿入した際に発生します。その結果、本来テキストとして表示されるはずの文字列が、ブラウザによってHTMLやJavaScriptコードとして解釈される可能性があります。
例えば、コメント機能のあるサイトで、ユーザーが入力した内容をサーバーが保存し、そのまま他ユーザーに表示する場合、検証やエスケープなしでHTMLに挿入してしまうと、攻撃者はJavaScriptを実行させる仕組みを埋め込むことができます。
簡単に言えば、XSSは「データ」と「コマンド」の混同です。本来、ユーザーは名前やコメント、検索キーワードなどの情報だけを送信できるべきですが、開発者のミスにより、ブラウザがそれらの一部を実行命令として認識してしまうのです。
「クロスサイトスクリプティング」という名称は歴史的経緯によるもので、必ずしもスクリプトが別サイトから移動する必要はありません。XSSの本質は、信頼されていないコードがユーザーの信頼するページの文脈で実行される点にあります。
一般的なサイト侵害では攻撃者がサーバーやデータベース、管理パネル、ファイルシステムへのアクセスを試みますが、XSSの主な標的はサイト訪問者のブラウザです。
攻撃者が必ずしもサーバー全体を制御する必要はなく、外部データが安全に処理されずにページに挿入される箇所を見つけるだけで十分です。被害者がそのページを開くことで悪意のあるコードがブラウザ上で実行されます。
この点で、XSSはSQLインジェクションなどとは異なります。SQLインジェクションでは不正なデータがデータベースへのリクエストに含まれますが、XSSではブラウザ側で実行可能なコードになります。サーバーサイドの脆弱性については、「SQLインジェクションとは?仕組みと防御策」の記事で詳しく解説しています。どちらも信頼できないデータの安全でない処理が原因ですが、影響の範囲が異なります。
XSS攻撃は、ウェブアプリケーションが信頼できないソースからデータを受け取ることから始まります。例えば、コメント、検索クエリ、URLパラメータ、ユーザー名など、ユーザーが変更可能な情報です。サイトがこれらのデータを安全に処理せずにページへ挿入すると、ブラウザはそれをテキストではなくHTMLやJavaScriptとして認識してしまう場合があります。
このとき、ページ生成の過程でサーバーやクライアント側のJavaScriptが受け取った値を、ブラウザがHTMLやコードとして期待する場所に配置してしまうのが主なミスです。これにより、細工されたデータがドキュメント構造を変えたり、予期しない命令を実行させたりします。
ブラウザは、どのコードがサイト開発者によるものか、攻撃者によるものかを区別できません。JavaScriptがページ内部に挿入され、保護機構でブロックされない場合、そのサイトの文脈で実行されます。
典型的な流れとしては、フォームやリクエストパラメータから始まります。ユーザーがデータを送信し、アプリケーションがそれを受け取り、HTMLページへ返します。正しい処理がされていれば、特殊文字はテキストとして表示されます。
エスケープが行われていないと、内容がHTML構造を変えてしまい、ブラウザは挿入された要素やイベントハンドラも解釈します。問題は入力自体の有無ではなく、どの文脈で、どのように出力されるかにあります。
データがサーバーに保存されなくても、アドレスバーや他のソースから値を取り出してDOMに安全でない方法で挿入すれば、XSSは発生します。サーバーサイドレンダリングの従来型サイトでも、JavaScript主体のモダンなウェブアプリでも同様です。
XSSの危険性や影響範囲は、サイトやブラウザの設定によって異なります。悪意のあるスクリプトは、ページ内容の読取やインターフェイス要素の変更、ユーザー行動の追跡、開いているタブ名義でのリクエスト送信などが可能です。
たとえば、攻撃者は偽のログインフォームや本物そっくりの要素を表示し、ユーザーを騙して情報を入力させることができます。本物のサイト内部で行われるため、ユーザーは違和感を覚えにくいのが特徴です。
また、JavaScriptはそのページで許可されている権限すべてで動作します。ユーザーが既にログイン済みの場合、ブラウザはそのセッション情報を同一サイトへのリクエストに自動で添付するため、XSSは情報の窃取だけでなく、被害者名義の一部操作も可能となります。
特に有名なのがセッションクッキーの窃取です。ログイン後、サイトはセッションIDを発行し、ブラウザに保存させます。これにより毎回パスワードを入力せずに済んでいます。
このクッキーがJavaScriptから参照可能な場合、XSS攻撃により値を盗み、攻撃者へ送信することができます。盗まれたIDは、場合によってはパスワードなしでユーザーになりすますのに使われます。
近年はHttpOnly属性によってJavaScriptからの直接読み取りは防げますが、XSS自体の本質的な解決にはなりません。悪意あるコードは依然としてページ内で動作し、許可された操作を実行できるため、セッション保護はXSS対策の補完でしかありません。
XSS攻撃は、悪意のあるコードがページへ到達する経路や実行タイミングによって以下の3種に大別されます。結果としてはどれも「他人のJavaScriptがブラウザで動く」ですが、脆弱性の原因や配布方法に違いがあります。
ストアドXSSは、悪意のあるデータがサイト側に保存され、その後他ユーザーにも自動的に表示されるケースです。コメント、メッセージ、プロフィール説明、フォーラム投稿など、データベースに記録されるあらゆるフィールドが対象になります。
この攻撃が特に危険なのは、攻撃者が毎回被害者へ専用リンクを送らずとも、一度埋め込めばそのページを開いた全ユーザーに影響が及ぶところです。人気ページの場合、1つのスクリプトで多くの被害者が出る可能性があります。
リフレクトXSSは、データがサイトに保存されず、リクエスト内で渡された値が即座に生成ページに反映されるパターンです。例えば、検索結果の見出しやエラーメッセージにURLパラメータがそのまま表示されるケースが該当します。
このタイプでは、攻撃者が用意したリンクをユーザーに踏ませる必要があるため、ソーシャルエンジニアリングやフィッシングメール、偽装リンクとの組み合わせで利用されることが多いです。
DOM XSSはブラウザ内部で発生し、クライアント側JavaScriptが値を処理する方法に問題があります。サーバーは安全なページを返しても、クライアント処理で脆弱性が生まれるケースです。
例えば、スクリプトがURLや#以降のフラグメント、リクエストパラメータなどから値を取得し、危険な方法でページに挿入する場合、攻撃者はDOM操作を通じてコード実行が可能になります。DOM XSSの特徴は、問題がアプリのクライアントロジックにあり、サーバーログだけでは発見が難しい点です。
ストアドXSSは「保存データ」、リフレクトXSSは「その場の入力」、DOM XSSは「ブラウザ内での危険な処理」に起因しますが、いずれも原因は「信頼できないデータがブラウザでコードとして解釈される」ことです。
XSSの最も危険な点は、悪意のあるコードがユーザーの信頼する本物のサイト内で動作することです。見た目は全く正常でも、ブラウザは開発者の意図しない処理を実行してしまうことがあります。
影響範囲はアプリケーションの機能によって異なります。あるサイトではインターフェイス変更のみで済みますが、別のサイトでは認証済みユーザー名義の操作や、ページ上の機密データ取得まで可能になることもあります。
悪意あるJavaScriptは、ページ上の情報を読み取ったり、インターフェイス要素の操作を監視したり、外部サーバーへデータ送信したりできます。特にマイページや管理画面など、外部からは本来アクセスできない情報がある部分が危険です。
ユーザーがすでにログインしていれば、ブラウザはそのまま信頼関係を維持し、リクエストへセッションデータを自動添付します。これにより、直接セッションIDが盗まれなくても、被害者名義の操作が可能となります。
XSSを使えば、ページのDOMを書き換えて新たな要素追加や既存要素の差し替えが可能です。本物そっくりのログインフォームやパスワード再入力画面、偽ボタンなどを表示し、ユーザーを別サイトへ誘導することもできます。
このような偽装の危険性は、実際のドメイン上で発生するため、ユーザーがサイトのURLだけを見て安心してしまう点にあります。XSSによってリンク書き換えや警告隠蔽、ページ内容の改ざん、リダイレクトといった多様な手段が実現されます。
HTTPSによる通信の暗号化はXSS対策にはなりません。HTTPSはブラウザとサーバー間の通信内容の盗聴や改ざんを防ぎますが、ページ内のJavaScriptが安全かどうかの検証は行いません。
もし正規サイトのサーバーが、既に悪意あるスクリプトを含むページを生成していたり、クライアントコードが脆弱なDOMを作成していた場合、ブラウザは暗号化されたHTTPS経由でデータを受け取っても、その内容をそのまま実行します。
この点がXSSと「中間者攻撃(MITM)」との大きな違いです。MITMは通信経路上でのデータ改ざんを狙いますが、XSSは信頼されたページ内部でコードが動く攻撃です。詳しくは「MITM攻撃とは?仕組みと対策」の記事をご覧ください。
クロスサイトスクリプティング対策の基本は、ユーザーや外部から受け取ったデータを自動的に安全と見なさないことです。アプリケーションはそれぞれのデータがどこに挿入され、ブラウザがどう解釈するかを常に管理する必要があります。
一つの万能な対策はなく、信頼性の高い防御には、正しいエスケープ処理、安全なDOM操作、スクリプト実行の制限、セッション保護などを組み合わせることが重要です。
最も重要なのは、ユーザーデータをHTMLコードとしてではなくテキストとして出力することです。コメントや名前、検索キーワードなど特殊文字を含む場合も、HTMLの一部ではなくテキストとして表示されるようにします。
ただし、エスケープ方法はコンテキストごとに異なります。HTML本体、属性、URL、JavaScript文字列など、それぞれ適切な処理が必要です。全てに同じ方法を使ったり、未加工の値を挿入すると脆弱性が生まれます。最近のテンプレートエンジンやフレームワークは自動的にエスケープを行いますが、手動でHTMLを挿入する際は特に注意が必要です。
入力値に許容フォーマットを設定することでリスクを減らせます。例えば年齢フィールドでHTMLタグを禁止したり、ファイル名に制御文字を含めないよう制限します。
記事エディタや書式付きコメントのようにHTMLを受け入れる必要がある場合、単に特殊文字を禁止するのではなく、許可された安全なタグと属性だけを残して他を除去する「サニタイズ(除去)」処理を施します。
ただし、入力値の検証やサニタイズは安全な出力処理の代替にはなりません。検証済みのデータも、後で異なるコンテキストで使われる場合があるため、常に出力時の保護が必要です。
Content Security Policy(CSP)は、ブラウザがどこからJavaScriptを読み込んだり実行するかを制限する仕組みです。CSPを正しく設定することで、XSSによる被害を大幅に軽減できますが、脆弱性そのものの根本解決にはなりません。
クライアント側JavaScriptの取り扱いにも注意が必要です。テキスト出力にはtextContentなど、安全なメソッドを使い、innerHTMLのような文字列をHTMLとして解釈する手法は、内容を完全に管理できる場合や事前クリーニングが済んでいる場合のみに限定しましょう。
XSSを完全に防げなくても、被害を抑える対策が有効です。セッションクッキーにはHttpOnly属性を付与し、JavaScriptからの直接参照を防止しましょう。またSecure属性でHTTPS通信時のみ送信とし、SameSite属性でクロスサイトリクエスト時の送信制御も行います。これらはXSS自体の防止にはなりませんが、セッションの窃取や悪用の一部シナリオを困難にします。
実際には、多層防御が効果的です。出力時エスケープ、HTMLサニタイズ、安全なDOM操作、CSPによるスクリプト制限、ブラウザ側でのクッキー保護を組み合わせることで、XSSリスクを最小化できます。
XSS攻撃は、サイトが信頼できないデータをユーザーのブラウザで実行可能なコードとして扱ってしまうことで発生します。悪意あるJavaScriptは保存データ、リクエストパラメータ、DOM処理など多様な経路でページに挿入されるため、ストアドXSS、リフレクトXSS、DOM XSSでそれぞれアプローチが異なります。
主な対策は、ユーザーデータをHTMLやJavaScriptと混在させず、安全なエスケープ、サニタイズ、慎重なDOM操作、CSP、クッキー保護など複数の方法を組み合わせることです。ウェブアプリケーションの設計段階からデータとコードを明確に分離することで、通常の入力欄がXSS攻撃の入口になるリスクを大きく減らすことができます。