【初心者向け】XSSとは?仕組み・種類・CSRFとの違い・対策方法をやさしく解説
Webシステムの開発やセキュリティに関わると、XSSという言葉を目にすることがあります。
「XSSはどのような攻撃なの?」
「JavaScriptが勝手に実行されるとはどういう意味?」
「CSRFとは何が違うの?」
「どのように対策すればよいの?」
このような疑問を持つIT業務初心者の方は少なくありません。
XSSとは、Webページへ攻撃者が用意した不正なスクリプトを混入させ、利用者のブラウザー上で実行させる攻撃です。
被害に遭うと、Cookieや入力情報を盗まれたり、偽のログイン画面を表示されたり、利用者の権限で勝手な操作を実行されたりする可能性があります。
この記事では、XSSの基本的な仕組み、代表的な種類、CSRFとの違い、実際の開発現場で必要な対策方法まで、IT業務に従事する初心者向けに分かりやすく解説します。
- XSSとは?
- XSSを一言で表すと?
- XSSを身近な例で考えてみよう
- JavaScriptとは?
- XSS攻撃の基本的な流れ
- XSSの簡単なイメージ
- XSSによって起こる主な被害
- Cookieを盗まれるとは?
- 偽のログイン画面を表示される被害
- Webページを書き換えられる被害
- XSSの代表的な3種類
- 反射型XSSとは?
- 格納型XSSとは?
- DOM Based XSSとは?
- 3種類のXSSの違い
- XSSとCSRFの違い
- XSSとSQLインジェクションの違い
- XSSとHTMLインジェクションの違い
- XSSが発生しやすい場所
- XSS対策の基本
- 出力時エスケープとは?
- なぜ入力時ではなく出力時に対策するの?
- HTML本文へ出力する場合
- HTML属性へ出力する場合
- JavaScript内へ直接埋め込まない
- URLへ出力する場合の注意
- テンプレートエンジンとは?
- サニタイズとは?
- 入力値検証だけでは不十分
- 許可リスト方式とは?
- innerHTMLが危険といわれる理由
- 危険になりやすいDOM操作
- Content Security Policyとは?
- インラインスクリプトとは?
- nonceとは?
- HttpOnly属性とは?
- Secure属性とは?
- SameSite属性とは?
- XSS対策でよくある間違い
- リッチテキスト入力ではどう対策する?
- Markdownを使えば安全?
- ファイルアップロードとXSS
- JSON APIでもXSSは発生する?
- URLのフラグメントとDOM Based XSS
- フレームワークを使えばXSSは防げる?
- 保存する前にエスケープすればよい?
- XSS対策の実装手順
- XSS対策のテスト方法
- コードレビューで確認するポイント
- 管理画面のXSSが危険な理由
- XSSが見つかったときの対応
- よくある質問
- IT業務初心者が覚えておきたいポイント
- 関連して覚えておきたい用語
- まとめ
XSSとは?
XSSは、Cross-Site Scriptingの略です。
日本語では、クロスサイトスクリプティングと呼ばれます。
本来であれば、略称はCSSになりそうですが、CSSはすでにWebページの見た目を整えるCascading Style Sheetsの略称として使われています。
そのため、Crossの「Cross」を「X」で表し、XSSと呼ばれています。
XSSでは、Webサイトが利用者から受け取った入力内容を安全に処理せず、Webページへそのまま表示することなどが原因で、不正なJavaScriptが実行されます。
攻撃者が不正な入力を送る ↓ Webサイトが入力を安全に処理せず表示 ↓ 利用者がページを開く ↓ 利用者のブラウザーで不正なスクリプトが実行される
XSSを一言で表すと?
XSSを一言で表すと、Webサイトへ不正なプログラムを紛れ込ませ、利用者のブラウザーで動かす攻撃です。
攻撃者のサーバー上でプログラムが実行されるのではなく、被害者が利用しているブラウザー上で実行される点が重要です。
ブラウザーから見ると、正規のWebサイトから送られたスクリプトに見える場合があります。
そのため、正規サイトの権限を使ってCookieやページ内の情報へアクセスされる可能性があります。
XSSを身近な例で考えてみよう
XSSは、店内の掲示板へ悪意のある命令書を貼り、掲示板を見た人にその命令を実行させるような攻撃です。
通常、掲示板には利用者からのお知らせやコメントが表示されます。
しかし、掲示内容を確認せずに何でも掲示してしまうと、攻撃者が次のような命令を書き込む可能性があります。
この掲示を読んだ人は、財布の中身を指定場所へ送ってください
XSSでも、攻撃者が入力した内容をWebサイトが安全に処理せず表示すると、ブラウザーが単なる文章ではなく、実行すべきプログラムとして解釈することがあります。
JavaScriptとは?
JavaScriptは、Webページへ動きや機能を追加するためのプログラミング言語です。
例えば、次のような処理に利用されます。
- ボタンを押したときに画面を変更する
- 入力内容を確認する
- サーバーからデータを取得する
- メニューを開閉する
- 画面の一部だけを更新する
JavaScript自体は、多くのWebサイトで必要とされる正規の技術です。
XSSでは、このJavaScriptを攻撃者が悪用します。
XSS攻撃の基本的な流れ
XSS攻撃は、次のような流れで発生します。
- 攻撃者が不正なスクリプトを含むデータを送信する
- Webサイトが入力内容を安全に処理せず保存または表示する
- 利用者が該当ページを開く
- ブラウザーが不正な文字列をプログラムとして解釈する
- 利用者のブラウザー上で不正な処理が実行される
攻撃者 ↓ 不正な入力 Webサイト ↓ 安全に処理せず表示 利用者のブラウザー ↓ 不正なJavaScriptを実行
XSSの簡単なイメージ
例えば、掲示板へ次のような文字列を投稿できるとします。
<script>alert('XSS')</script>
Webサイトがこの内容を文字として表示せず、HTMLの一部としてそのまま出力すると、ブラウザーがscriptタグを実行する可能性があります。
この例では警告画面が表示されるだけですが、実際の攻撃では、より悪質な処理が実行される可能性があります。
なお、このコードは仕組みを理解するための単純な例です。実際のWebブラウザーやフレームワークでは、さまざまな防御機能が働く場合があります。
XSSによって起こる主な被害
- Cookieやセッション情報を盗まれる
- ログイン情報を入力させる偽フォームを表示される
- 入力した個人情報を盗まれる
- Webページの内容を書き換えられる
- 不正なWebサイトへ転送される
- 利用者の権限で操作を実行される
- マルウェアのダウンロードへ誘導される
- Webサイトの信用が低下する
Cookieを盗まれるとは?
Cookieは、ログイン状態や利用者の設定などをブラウザーへ保存する仕組みです。
Webサイトによっては、ログイン状態を識別するセッションIDがCookieへ保存されます。
XSSによってこの情報を取得されると、攻撃者が利用者になりすませる可能性があります。
利用者のCookie ↓ XSSで盗まれる 攻撃者 ↓ 利用者になりすます
ただし、CookieへHttpOnly属性が設定されていれば、JavaScriptからCookieへアクセスできないようにできます。
偽のログイン画面を表示される被害
XSSによって、正規のWebサイト内へ偽のログインフォームを表示される可能性があります。
利用者は正規サイトを開いているため、表示されたフォームを信用してIDやパスワードを入力してしまうかもしれません。
入力された情報が攻撃者へ送信されると、アカウントを乗っ取られる可能性があります。
Webページを書き換えられる被害
不正なJavaScriptによって、ブラウザー上に表示される内容を変更される場合があります。
例えば、次のような改ざんが考えられます。
- 振込先の口座番号を変更する
- 問い合わせ先を攻撃者の連絡先へ変える
- 偽の警告を表示する
- 不正な広告を表示する
- 正規のボタンを偽ボタンへ置き換える
サーバー上の元データが変更されていなくても、利用者のブラウザー上だけで表示を書き換えられる場合があります。
XSSの代表的な3種類
XSSは、主に次の3種類に分けられます。
- 反射型XSS
- 格納型XSS
- DOM Based XSS
反射型XSSとは?
反射型XSSは、URLやフォームへ入力された不正なデータが、Webページの応答へそのまま反映されることで発生するXSSです。
英語ではReflected XSSと呼ばれます。
例えば、検索画面で入力したキーワードを次のように表示するとします。
「パソコン」で検索しました
入力内容を安全に処理せず表示している場合、攻撃者が不正なスクリプトを含むURLを作成する可能性があります。
攻撃者が不正なURLを作る ↓ 利用者がURLを開く ↓ 入力内容がページへ反映される ↓ 不正なスクリプトが実行される
不正なデータがサーバーへ長期間保存されず、リクエストに対するレスポンスへ反射することから反射型と呼ばれます。
格納型XSSとは?
格納型XSSは、不正なスクリプトがデータベースなどへ保存され、そのデータを表示した複数の利用者へ影響するXSSです。
英語ではStored XSSまたはPersistent XSSと呼ばれます。
例えば、次のような機能が攻撃対象になる可能性があります。
- 掲示板
- コメント欄
- プロフィール
- 商品レビュー
- 問い合わせ内容の管理画面
- チャット
攻撃者が不正なコメントを投稿 ↓ データベースへ保存 ↓ 別の利用者がコメントを表示 ↓ 不正なスクリプトが実行
複数の利用者へ継続的に影響する可能性があるため、被害が大きくなりやすいXSSです。
DOM Based XSSとは?
DOM Based XSSは、ブラウザー内のJavaScriptが安全でない方法でデータを処理することで発生するXSSです。
DOMは、WebページのHTMLをプログラムから操作するための仕組みです。
この種類では、サーバー側のHTML生成ではなく、ブラウザー側のJavaScript処理に問題があります。
URLなどから値を取得 ↓ JavaScriptが値を安全に処理せずHTMLへ挿入 ↓ 不正なスクリプトが実行
例えば、URLの一部を取得してinnerHTMLへそのまま設定すると、DOM Based XSSにつながる可能性があります。
3種類のXSSの違い
| 種類 | 不正なデータの場所 | 主な特徴 |
|---|---|---|
| 反射型XSS | URLやリクエスト | 不正URLを開いた利用者へ影響する |
| 格納型XSS | データベースなど | 保存された内容を見た複数利用者へ影響する |
| DOM Based XSS | ブラウザー側の処理 | JavaScriptによるDOM操作が原因になる |
XSSとCSRFの違い
XSSと混同されやすい攻撃に、CSRFがあります。
| 項目 | XSS | CSRF |
|---|---|---|
| 正式名称 | Cross-Site Scripting | Cross-Site Request Forgery |
| 攻撃内容 | 不正なスクリプトをブラウザーで実行させる | 利用者へ意図しないリクエストを送信させる |
| 主に悪用するもの | 安全でないHTML出力やDOM操作 | Cookieが自動送信される仕組み |
| 主な被害 | 情報窃取、画面改ざん、偽フォーム表示 | 設定変更、購入、送金、投稿 |
| 代表的な対策 | 出力時エスケープ、CSP | CSRFトークン、SameSite Cookie |
XSSは、Webページ内で攻撃者のプログラムを動かす攻撃です。
CSRFは、ログイン中の利用者へ意図しない操作を実行させる攻撃です。
なお、XSSが存在すると、ページ内の情報やCSRFトークンを読み取られる可能性があるため、CSRF対策だけでは防げない場合があります。
XSSとSQLインジェクションの違い
| 項目 | XSS | SQLインジェクション |
|---|---|---|
| 攻撃対象 | Webブラウザー | データベース |
| 悪用するもの | HTMLやJavaScriptの不適切な処理 | SQL文の不適切な組み立て |
| 主な被害 | Cookie窃取、画面改ざん | データ漏えい、改ざん、削除 |
| 代表的な対策 | 出力時エスケープ | プレースホルダー、パラメーター化クエリ |
どちらも利用者から受け取った入力を安全に扱わないことで発生しますが、攻撃対象と対策が異なります。
XSSとHTMLインジェクションの違い
HTMLインジェクションは、不正なHTMLをWebページへ挿入する攻撃です。
必ずしもJavaScriptが実行されるとは限りません。
例えば、偽の入力フォームやリンクを表示して利用者をだます場合があります。
XSSは、主にスクリプトを実行させる攻撃ですが、両者は関連が深く、同じ入力処理の不備が原因になることがあります。
XSSが発生しやすい場所
- 検索キーワードの表示
- 掲示板やコメント欄
- プロフィールの自己紹介
- 商品レビュー
- 問い合わせ内容の管理画面
- チャットメッセージ
- エラーメッセージ
- URLパラメーターの表示
- ファイル名の表示
- 管理者向けログ確認画面
一般利用者向け画面だけでなく、管理画面でもXSSが発生する可能性があります。
XSS対策の基本
XSS対策として、主に次の方法があります。
- 出力時にエスケープする
- 安全なテンプレートエンジンを利用する
- 危険なHTMLをサニタイズする
- innerHTMLなどの危険な機能を安易に使わない
- Content Security Policyを設定する
- CookieへHttpOnly属性を設定する
- 入力値を用途に応じて検証する
- フレームワークの標準機能を利用する
出力時エスケープとは?
出力時エスケープとは、利用者から受け取った文字列を、HTMLやJavaScriptとして実行されない安全な形式へ変換して表示することです。
例えば、次の入力があるとします。
<script>alert('XSS')</script>
そのままHTMLへ出力すると、scriptタグとして解釈される可能性があります。
エスケープすると、次のように単なる文字として表示されます。
<script>alert('XSS')</script>
ブラウザーには、次の文字列として表示されます。
重要なのは、プログラムとして実行させず、文字として表示することです。
なぜ入力時ではなく出力時に対策するの?
同じデータでも、出力する場所によって必要な処理が異なるためです。
例えば、次の出力場所では、それぞれ異なる安全対策が必要になる場合があります。
- HTML本文
- HTML属性
- JavaScript内
- URL
- CSS
HTML本文向けのエスケープを、そのままJavaScript内へ使えば安全とは限りません。
データを出力する文脈に合ったエスケープを行うことが重要です。
HTML本文へ出力する場合
HTML本文へ利用者の入力を表示する場合は、HTML特殊文字をエスケープします。
代表的な文字には次のものがあります。
| 文字 | 意味 |
|---|---|
| < | タグの開始として解釈される |
| > | タグの終了として解釈される |
| & | 文字参照として使われる |
| " | 属性値の区切りに使われる |
| ' | 属性値やJavaScript文字列に使われる |
HTML属性へ出力する場合
利用者の入力をHTML属性へ入れる場合は、引用符を含めて適切にエスケープします。
例えば、次のような出力です。
<input value="利用者の入力">
属性値を引用符で囲み、属性向けのエスケープを行う必要があります。
利用者の入力をイベント属性へ入れる設計は避けます。
<button onclick="利用者の入力">
onclickなどへ入力値を直接埋め込むと、XSSの危険が高まります。
JavaScript内へ直接埋め込まない
サーバー側のデータをJavaScriptコード内へ直接埋め込む場合は、特に注意が必要です。
<script>
const userName = '利用者の入力';
</script>
引用符や改行などを悪用されると、意図しないJavaScriptを実行される可能性があります。
可能であれば、データ属性やJSONとして安全に受け渡し、信頼できるライブラリで解析します。
URLへ出力する場合の注意
リンク先のURLへ利用者の入力を使用する場合は、URLの形式やスキームを検証します。
例えば、次のような危険なスキームを許可しないようにします。
javascript:
httpやhttpsなど、システムで許可するスキームを明確に決めます。
テンプレートエンジンとは?
テンプレートエンジンは、プログラムのデータをHTMLへ埋め込むための仕組みです。
多くのテンプレートエンジンには、自動エスケープ機能があります。
利用者の入力を通常の方法で出力すると、自動的にHTMLエスケープしてくれる場合があります。
ただし、次のような機能を使うと、自動エスケープを無効化する場合があります。
- raw出力
- 安全なHTMLとして扱う指定
- エスケープ無効化
- HTMLを直接挿入する機能
自動エスケープを無効にする場合は、本当に安全なデータか慎重に確認する必要があります。
サニタイズとは?
サニタイズとは、入力されたHTMLから危険なタグや属性を取り除き、安全な内容だけを残す処理です。
例えば、ブログやコメント欄で一部のHTMLを許可したい場合に利用します。
許可するもの ・pタグ ・strongタグ ・ulタグ 削除するもの ・scriptタグ ・onclick属性 ・危険なURL
HTMLを一切許可しない場合は、基本的にエスケープして文字として表示します。
一部のHTMLを許可する必要がある場合に、実績のあるサニタイズライブラリを利用します。
入力値検証だけでは不十分
入力値検証は、文字数や形式を確認するために重要です。
例えば、年齢欄には数字だけ、メールアドレス欄にはメール形式だけを許可します。
ただし、入力値検証だけでXSSを完全に防ぐことはできません。
正常な文字列でも、出力する場所によって危険になる場合があります。
入力値検証と出力時エスケープを組み合わせることが重要です。
許可リスト方式とは?
入力値検証では、禁止する文字を並べるよりも、許可する形式を明確に決める方法が有効です。
これを許可リスト方式と呼びます。
例えば、商品数量であれば、次のように制限します。
- 整数だけ許可
- 1以上100以下
- 記号やHTMLタグは許可しない
ただし、氏名やコメントなど自由入力が必要な項目では、入力を過度に制限せず、出力時エスケープを正しく行います。
innerHTMLが危険といわれる理由
JavaScriptのinnerHTMLは、指定した文字列をHTMLとして解釈して画面へ挿入します。
element.innerHTML = userInput;
userInputへ不正なHTMLが含まれていると、XSSにつながる可能性があります。
単なる文字列を表示したい場合は、textContentなどを利用します。
element.textContent = userInput;
textContentは、入力内容をHTMLとしてではなく、文字として扱います。
危険になりやすいDOM操作
次のような機能へ信頼できないデータを渡す場合は注意が必要です。
- innerHTML
- outerHTML
- insertAdjacentHTML
- document.write
- eval
- setTimeoutへ文字列を渡す処理
- setIntervalへ文字列を渡す処理
安全な代替機能を利用し、利用者の入力をコードとして実行しないようにします。
Content Security Policyとは?
Content Security Policyは、ブラウザーへ「このWebページで、どこから読み込んだスクリプトを実行してよいか」を伝える仕組みです。
略してCSPと呼ばれます。
HTTPレスポンスヘッダーなどで設定します。
Content-Security-Policy: script-src 'self'
この例では、基本的に同じWebサイトから配信されたスクリプトだけを許可します。
CSPで制限できるもの
- JavaScriptの読み込み元
- CSSの読み込み元
- 画像の読み込み元
- フレームの表示元
- 外部サービスへの通信先
- インラインスクリプトの実行
CSPはXSSの被害を抑える有効な防御策ですが、出力時エスケープの代わりにはなりません。
インラインスクリプトとは?
インラインスクリプトとは、HTML内へ直接記述されたJavaScriptです。
<script>
alert('hello');
</script>
また、onclickなどのイベント属性もインラインスクリプトに含まれます。
<button onclick="alert('hello')">実行</button>
CSPでインラインスクリプトを制限すると、XSSによって挿入されたJavaScriptを実行しにくくできます。
nonceとは?
nonceは、許可したscriptタグだけを実行するために利用できる、一時的で推測困難な値です。
サーバーがレスポンスごとに異なるnonceを生成し、CSPとscriptタグへ設定します。
<script nonce="ランダムな値">
// 許可されたスクリプト
</script>
正しいnonceが付いていないスクリプトは、ブラウザーによって拒否されます。
nonceはレスポンスごとに安全な乱数で生成し、固定値を使い回さないようにします。
HttpOnly属性とは?
HttpOnlyは、JavaScriptからCookieへアクセスできないようにする属性です。
Set-Cookie: session=値; HttpOnly
HttpOnlyを設定すると、XSSが発生した場合でも、JavaScriptからセッションCookieを直接取得しにくくできます。
ただし、攻撃者のスクリプトが利用者のブラウザー上で動いている場合、正規サイトへリクエストを送らせるなど、別の被害が発生する可能性があります。
そのため、HttpOnlyは補助的な対策であり、XSSそのものを防ぐ対策ではありません。
Secure属性とは?
Secure属性を設定したCookieは、原則としてHTTPS通信でのみ送信されます。
Set-Cookie: session=値; Secure
HTTP通信でCookieが送信される危険を減らせます。
XSS対策としては、HttpOnlyやSameSiteなどと組み合わせて利用します。
SameSite属性とは?
SameSite属性は、異なるサイトからのリクエストでCookieを送信するかを制御します。
主にCSRF対策として利用されます。
XSSを直接防ぐ機能ではありませんが、Cookieの安全性を高めるために、HttpOnly・Secureと合わせて設定されます。
XSS対策でよくある間違い
scriptという文字だけ削除する
XSSはscriptタグだけで発生するとは限りません。
イベント属性、危険なURL、SVGなど、複数の方法が悪用される可能性があります。
特定の文字列を削除するだけの対策は回避されやすいため、適切なエスケープやサニタイズを使用します。
入力時に記号をすべて削除する
氏名、住所、コメントなどでは、正当な入力に記号が含まれることがあります。
入力を壊すだけでなく、出力場所によっては十分な対策にならない可能性があります。
サーバー側だけ対策すればよいと思う
DOM Based XSSは、ブラウザー側のJavaScript処理が原因で発生する場合があります。
フロントエンドのコードも確認する必要があります。
HTMLエスケープだけですべて安全だと思う
JavaScript、URL、CSSなどへ出力する場合は、HTML本文とは異なる対策が必要です。
CSPだけ設定すればよいと思う
CSPは重要な多層防御ですが、設定ミスや許可範囲によっては攻撃を防げない場合があります。
出力時エスケープや安全なDOM操作も必要です。
管理画面だから安全だと思う
問い合わせ内容や利用者名などに不正な入力が含まれていると、管理者が画面を開いたときにXSSが実行される可能性があります。
リッチテキスト入力ではどう対策する?
ブログやメール作成画面などでは、太字、リンク、見出しなど、一部のHTMLを許可する場合があります。
すべてをエスケープすると装飾できないため、許可するタグや属性だけを残すサニタイズが必要です。
例えば、次のような許可リストを作ります。
- pタグ
- strongタグ
- emタグ
- ul・ol・liタグ
- 安全なhref属性を持つaタグ
scriptタグ、イベント属性、危険なURLなどは削除します。
独自実装ではなく、実績のあるHTMLサニタイズライブラリを利用することが重要です。
Markdownを使えば安全?
Markdownは、簡単な記号で文章を装飾できる記法です。
ただし、MarkdownからHTMLへ変換するライブラリによっては、生のHTMLを許可する設定があります。
生のHTMLが許可されていると、XSSにつながる可能性があります。
HTMLを無効化するか、変換後のHTMLを安全なライブラリでサニタイズします。
ファイルアップロードとXSS
画像や文書のアップロード機能でも、XSSに注意が必要です。
例えば、次のようなファイルが問題になる可能性があります。
- 不正なスクリプトを含むSVG
- HTMLファイル
- 実際の内容と拡張子が異なるファイル
- 不正なファイル名
対策として、次の内容を確認します。
- 許可するファイル形式を限定する
- MIMEタイプと実際の内容を確認する
- 安全なファイル名へ変更する
- アップロード先をアプリ本体と分離する
- Content-Typeを正しく設定する
- Content-Dispositionを適切に設定する
JSON APIでもXSSは発生する?
JSONを返すAPI自体では、通常、HTMLが直接実行されるわけではありません。
しかし、APIから受け取った文字列をフロントエンドがinnerHTMLなどへ挿入すると、XSSが発生する可能性があります。
APIからデータ取得 ↓ フロントエンドがinnerHTMLへ設定 ↓ XSSが発生
APIと画面の両方で、データをどのように扱っているか確認する必要があります。
URLのフラグメントとDOM Based XSS
URLの「#」より後ろの部分をフラグメントと呼びます。
Example Domain
フラグメントは通常、サーバーへ送信されず、ブラウザー内のJavaScriptで利用されます。
JavaScriptがフラグメントの値を安全に処理せずHTMLへ挿入すると、DOM Based XSSが発生する可能性があります。
フレームワークを使えばXSSは防げる?
多くのWebフレームワークやフロントエンドライブラリには、自動エスケープ機能があります。
ただし、次のような場合はXSSが発生する可能性があります。
- 自動エスケープを無効にした
- HTMLを直接挿入する機能を使った
- 危険なURLを許可した
- 古いライブラリに脆弱性がある
- DOM操作を独自実装した
- サードパーティ製スクリプトが侵害された
フレームワークを使っているだけで安心せず、安全な使い方を確認します。
保存する前にエスケープすればよい?
データベースへ保存する前にHTMLエスケープすると、同じデータを別の用途で使うときに問題になる場合があります。
例えば、次の用途で同じデータを使う可能性があります。
- HTML画面
- CSVファイル
- メール
- JSON API
保存時は元のデータを適切に管理し、表示する場所に応じて出力時に安全な処理を行う方法が基本です。
ただし、リッチテキストなどは保存前または表示前にサニタイズする設計もあります。
XSS対策の実装手順
- 利用者入力が表示される場所を洗い出す
- 出力先の文脈を確認する
- テンプレートエンジンの自動エスケープを有効にする
- HTML本文・属性・URLなどに合った処理を行う
- 危険なDOM操作を安全な方法へ変更する
- HTMLを許可する場所ではサニタイズする
- CSPを設定する
- CookieへHttpOnly・Secure属性を設定する
- フレームワークやライブラリを更新する
- セキュリティテストを実施する
XSS対策のテスト方法
開発やレビューでは、次のような場所へ特殊文字やHTMLタグを入力して確認します。
- 検索欄
- コメント欄
- プロフィール
- 問い合わせフォーム
- ファイル名
- URLパラメーター
- 管理画面に表示される項目
主に次の内容を確認します。
- 入力したHTMLが文字として表示されるか
- scriptタグが実行されないか
- イベント属性が実行されないか
- 危険なURLがリンクとして利用されないか
- DOM操作で不正なHTMLが挿入されないか
- CSP違反が記録されているか
コードレビューで確認するポイント
- 利用者入力をHTMLへ直接連結していないか
- innerHTMLを使用していないか
- 自動エスケープを無効化していないか
- raw出力を安易に使っていないか
- URLスキームを検証しているか
- HTMLサニタイズを独自実装していないか
- JavaScript内へ入力値を直接埋め込んでいないか
- CSPが適切に設定されているか
- CookieへHttpOnlyが設定されているか
- 古いライブラリを利用していないか
管理画面のXSSが危険な理由
問い合わせフォームや会員情報に不正なスクリプトを保存し、管理者が管理画面を開いたときに実行させる攻撃があります。
管理者は一般利用者より強い権限を持っているため、次のような被害につながる可能性があります。
- 管理者セッションの悪用
- 利用者情報の閲覧
- 設定変更
- 新しい管理者アカウントの作成
- データの削除
- サイト全体の改ざん
利用者向け画面だけでなく、管理画面でも必ず出力時エスケープを行います。
XSSが見つかったときの対応
XSSの脆弱性が見つかった場合は、次のような対応を行います。
- 影響する画面と入力項目を特定する
- 反射型・格納型・DOM Basedのどれか確認する
- 危険な出力処理を修正する
- 保存済みの不正データを調査・削除する
- セッション情報への影響を確認する
- 必要に応じてセッションを無効化する
- アクセスログやCSPレポートを調査する
- 類似箇所を横断的に確認する
- 修正後に再テストする
格納型XSSの場合は、不正なデータがすでにデータベースへ保存されている可能性があるため、コード修正だけでなく保存データの確認も必要です。
よくある質問
XSSではサーバーが攻撃されるのですか?
主に攻撃されるのは、Webサイトを見ている利用者のブラウザーです。
ただし、管理者のブラウザーで実行されると、管理機能を悪用され、結果的にサーバー上のデータへ影響する可能性があります。
JavaScriptを無効にすればXSSを防げますか?
利用者側でJavaScriptを無効にすれば一部の攻撃は動作しなくなります。
しかし、多くのWebサイトが正常に使えなくなるため、Webサイト側で正しく対策する必要があります。
入力値からscriptタグを削除すれば十分ですか?
十分ではありません。
XSSはscriptタグ以外のタグや属性、URLなどを悪用して発生する可能性があります。
HTMLタグをすべて禁止すれば安全ですか?
HTMLを必要としない入力では、エスケープして文字として表示する方法が有効です。
ただし、URLやJavaScriptなど別の場所へ出力する場合は、出力先に合った対策も必要です。
HttpOnlyを設定すればXSSは防げますか?
XSSそのものは防げません。
JavaScriptによるCookieの直接取得を防ぎやすくする補助的な対策です。
CSPを設定すればXSS対策は不要ですか?
不要にはなりません。
CSPは被害を抑える多層防御であり、出力時エスケープや安全なDOM操作も必要です。
ReactやVueを使えばXSSは発生しませんか?
通常の文字列表示では自動エスケープされることが多いですが、HTMLを直接挿入する機能や危険なURLを使うとXSSが発生する可能性があります。
バックエンドだけ対策すればよいですか?
いいえ。DOM Based XSSはフロントエンドのJavaScriptが原因になるため、ブラウザー側のコードも確認する必要があります。
XSSとCSRFはどちらが危険ですか?
どちらも重大な被害につながる可能性があり、単純には比較できません。
XSSがあると、CSRFトークンを読み取ったり、利用者の代わりに操作したりできる場合があるため、XSS対策は特に重要です。
URLへ入力値を入れるだけでもXSSになりますか?
入力値をリンク先として利用し、javascriptスキームなどを許可するとXSSにつながる可能性があります。
許可するURL形式やスキームを検証する必要があります。
管理者しか見ない画面でも対策は必要ですか?
必要です。
攻撃者が問い合わせ内容や会員情報などへ不正なデータを登録し、管理者が画面を開いたときに実行させる可能性があります。
IT業務初心者が覚えておきたいポイント
- XSSは利用者のブラウザーで不正なスクリプトを実行させる攻撃
- Cross-Site Scriptingの略
- 反射型・格納型・DOM Based XSSがある
- Cookie窃取、偽フォーム表示、画面改ざんなどの被害がある
- 代表的な原因は利用者入力の安全でない出力
- 基本対策は出力時エスケープ
- 出力する場所に合ったエスケープが必要
- HTMLを許可する場合はサニタイズする
- innerHTMLなどの危険なDOM操作を避ける
- CSPは有効な多層防御
- HttpOnlyはCookie窃取リスクを下げる補助対策
- フレームワークの自動エスケープを無効化しない
- 一般画面だけでなく管理画面も対策する
関連して覚えておきたい用語
- JavaScript
- HTML
- DOM
- 反射型XSS
- 格納型XSS
- DOM Based XSS
- 出力時エスケープ
- サニタイズ
- Content Security Policy
- CSP
- nonce
- Cookie
- HttpOnly
- Secure属性
- CSRF
- SQLインジェクション
まとめ
XSSとは、Webページへ不正なスクリプトを混入させ、利用者のブラウザー上で実行させる攻撃です。
利用者から受け取った入力内容を安全に処理せず、HTMLやJavaScriptとして出力することで発生します。
代表的な種類には、反射型XSS、格納型XSS、DOM Based XSSがあります。
XSSによって、Cookieや入力情報の窃取、偽ログイン画面の表示、Webページの改ざん、利用者権限での不正操作などが行われる可能性があります。
基本的な対策は、利用者の入力を出力する場所に応じて正しくエスケープすることです。
加えて、安全なテンプレートエンジン、HTMLサニタイズ、CSP、HttpOnly Cookie、危険なDOM操作の回避などを組み合わせます。
まずは、「XSSは、Webサイトへ不正なプログラムを紛れ込ませ、利用者のブラウザーで実行させる攻撃」と理解しておきましょう。
