クロスサイトスクリプティングの防止
Cross Site Scripting Prevention Cheat Sheet
- 原典
- Cross Site Scripting Prevention Cheat Sheet(OWASP Cheat Sheet Series)
- 原文
- GitHub 上の Markdown
- 底本
bac04fb5(2026-07-29 時点)- ライセンス
- CC BY-SA 4.0(原典と同一。訳文も同ライセンスで再配布できます)
はじめに
このチートシートは、開発者が XSS 脆弱性を防ぐための手引きである。
クロスサイトスクリプティング(XSS)という呼び名は適切ではない。 この用語はもともと、サイトをまたいでデータを盗むことを主眼とした初期の攻撃形態に由来する。 その後、この語は実質的にあらゆるコンテンツの注入を含むように広がった。 XSS 攻撃は深刻であり、アカウントのなりすまし、利用者の行動の観測、外部コンテンツの読み込み、機微なデータの窃取などにつながりうる。
このチートシートは、XSS を防ぐ、あるいはその影響を限定する手法を扱う。単一の手法で XSS を解決できるものはないので、防御手法を適切に組み合わせる必要がある。
フレームワークによる保護
幸いなことに、現代の Web フレームワークで構築されたアプリケーションでは XSS の不具合が少ない。 これらのフレームワークは開発者を良い慣行へ導き、テンプレート化や自動エスケープなどによって XSS の緩和を助ける。 ただし開発者は、フレームワークを安全でない使い方をすると問題が生じうることを知っておく必要がある。
- DOM を直接操作するためにフレームワークが用意している「抜け道」
- HTML をサニタイズせずに使う React の
dangerouslySetInnerHTML - React は専用の検証なしに
javascript:やdata:の URL を扱えない - Angular の
bypassSecurityTrustAs*系の関数 - Lit の
unsafeHTML関数 - Polymer の
inner-h-t-m-l属性とhtmlLiteral関数 - テンプレートインジェクション
- 更新されていないフレームワークのプラグインやコンポーネント
- その他
現代の Web フレームワークを使うときは、そのフレームワークがどのように XSS を防ぐのか、どこに穴があるのかを知っておく必要がある。 フレームワークの保護の外側で何かをしなければならない場面は必ずあり、そこでは出力エンコーディングと HTML サニタイズが決定的に重要になる。 OWASP は React、Vue、Angular それぞれに固有のチートシートを用意する予定である。
XSS 防御の考え方
XSS 攻撃が成功するには、攻撃者が悪意あるコンテンツを Web ページに挿入して実行させられる必要がある。 したがって、Web アプリケーション内のすべての変数を保護しなければならない。 すべての変数が検証を通り、そのうえでエスケープまたはサニタイズされる状態を、完全なインジェクション耐性(perfect injection resistance)と呼ぶ。 この処理を通らない変数は、いずれも弱点になりうる。 フレームワークは、変数が正しく検証され、エスケープまたはサニタイズされる状態を作りやすくしてくれる。
しかし完璧なフレームワークは存在せず、React や Angular のような広く使われるフレームワークにもセキュリティ上の穴が残っている。 出力エンコーディングと HTML サニタイズが、その穴を埋める助けになる。
出力エンコーディング
利用者が入力したデータをそのままの形で安全に表示する必要があるときは、出力エンコーディングが推奨される。 変数はテキストとして扱われ、コードとして解釈されてはならない。 この節では、出力エンコーディングの各形式、それをどこで使うのか、そして動的な変数をそもそも置いてはならない箇所を扱う。
まず、利用者が入力したとおりにデータを表示したい場合は、フレームワークの既定の出力エンコーディング保護から始める。 自動的なエンコーディングとエスケープの機能は、ほとんどのフレームワークに組み込まれている。
フレームワークを使っていない場合、あるいはフレームワークの穴を埋める必要がある場合は、出力エンコーディングのライブラリを使うべきである。 ユーザーインタフェースで使う各変数は、出力エンコーディング関数を通す。 出力エンコーディングのライブラリの一覧は付録にある。
出力エンコーディングの方式が多数あるのは、ブラウザが HTML、JS、URL、CSS をそれぞれ異なる方法で解析するからである。 誤った方式を使うと、弱点を持ち込むか、アプリケーションの機能を壊すことになる。
「HTML コンテキスト」の出力エンコーディング
「HTML コンテキスト」とは、<div> や <b> のような基本的な HTML タグの間に変数を挿入することを指す。
たとえば次のような箇所である。
<div> $varUnsafe </div>
攻撃者は $varUnsafe として描画されるデータを書き換えられる。
これは Web ページに攻撃を追加することにつながる。
たとえば次のようになる。
<div> <script>alert`1`</script> </div> // Example Attack
HTML コンテキストへ変数を安全に追加するには、その変数に HTML エンティティエンコーディングを適用する。
特定の文字に対するエンコード結果の例を以下に示す。
JavaScript から HTML へ書き込む場合は、.textContent 属性を検討する。
これは安全なシンクであり、自動的に HTML エンティティエンコーディングを行う。
& &
< <
> >
" "
' '
「HTML 属性コンテキスト」の出力エンコーディング
「HTML 属性コンテキスト」は、変数が HTML 属性の値に置かれる場合に生じる。 ハイパーリンクの変更、要素の非表示、画像の代替テキストの付与、インライン CSS スタイルの変更などで、これをしたくなる。 ほとんどの HTML 属性に置く変数には、HTML 属性エンコーディングを適用するべきである。 安全な HTML 属性の一覧は「安全なシンク」の節にある。
<div attr="$varUnsafe">
<div attr=”*x” onblur=”alert(1)*”> // Example Attack
変数を " や ' のような引用符で囲むことが決定的に重要である。
引用符で囲むと変数の動作するコンテキストを変えることが難しくなり、XSS の防止につながる。
また引用符で囲むことでエンコードが必要な文字集合が大幅に減り、アプリケーションの信頼性が上がってエンコードの実装も容易になる。
JavaScript から HTML 属性へ書き込む場合は、.setAttribute と [attribute] を検討する。
これらは自動的に HTML 属性エンコーディングを行う。
属性名が id や class のようにハードコードされた無害なものである限り、これらは安全なシンクである。
一般に onClick のように JavaScript を受け付ける属性は、信頼できない属性値とともに使うのは安全ではない。
「JavaScript コンテキスト」の出力エンコーディング
「JavaScript コンテキスト」は、変数がインライン JavaScript に置かれ、それが HTML 文書に埋め込まれる状況を指す。 これは、Web ページに埋め込む独自の JavaScript を多用しているプログラムでよく生じる。
しかし JavaScript の中で変数を置ける「安全な」場所は、「引用符で囲まれたデータ値」の内側だけである。 他のコンテキストはすべて安全ではなく、変数のデータを置くべきではない。
「引用符で囲まれたデータ値」の例を示す。
<script>alert('$varUnsafe’)</script>
<script>x=’$varUnsafe’</script>
<div onmouseover="'$varUnsafe'"</div>
すべての文字を \xHH 形式でエンコードする。
エンコーディングのライブラリには、この機能を提供する EncodeForJavaScript などの関数がしばしば用意されている。
最小限のエンコードで済む適切な JavaScript の使い方の例は、OWASP Java Encoder の JavaScript エンコーディングの例を参照する。
JSON については、XSS を防ぐために Content-Type ヘッダが text/html ではなく application/json であることを確認する。
「CSS コンテキスト」の出力エンコーディング
「CSS コンテキスト」はインライン CSS に置かれる変数を指し、利用者に Web ページの見た目を変えさせたい場合によく現れる。 CSS は意外なほど強力であるため、多くの種類の攻撃に使われてきた。 変数は CSS のプロパティ値にのみ置くべきである。他の「CSS コンテキスト」は安全ではなく、変数のデータを置くべきではない。
<style> selector { property : $varUnsafe; } </style>
<style> selector { property : "$varUnsafe"; } </style>
<span style="property : $varUnsafe">Oh no</span>
JavaScript から CSS のプロパティを変更する場合は、style.property = x の使用を検討する。
これは安全なシンクであり、置かれたデータを自動的に CSS エンコードする。
CSS プロパティに変数を挿入するときは、インジェクション攻撃を防ぐためにデータが適切にエンコードおよびサニタイズされていることを確認する。 セレクタやその他の CSS コンテキストに変数を直接置くことは避ける。
「URL コンテキスト」の出力エンコーディング
「URL コンテキスト」は URL に置かれる変数を指す。 最もよくあるのは、開発者が URL の基底部分にパラメータや URL フラグメントを追加し、それが表示されたり何らかの処理に使われたりする場合である。 この場面では URL エンコーディングを使う。
<a href="http://www.owasp.org?test=$varUnsafe">link</a >
すべての文字を %HH のエンコード形式でエンコードする。
JS や CSS と同様に、属性は必ず引用符で囲む。
よくある誤り
同じ URL を異なるコンテキストで使う状況が生じる。
最もよくあるのは、<a> タグの href 属性や src 属性に URL を入れる場合である。
この場面では、URL エンコーディングを行ったうえで HTML 属性エンコーディングを行う。
url = "https://site.com?data=" + urlencode(parameter)
<a href='attributeEncode(url)'>link</a>
JavaScript で URL のクエリ値を組み立てる場合は、window.encodeURIComponent(x) の使用を検討する。
これは安全なシンクであり、置かれたデータを自動的に URL エンコードする。
危険なコンテキスト
出力エンコーディングは完全ではなく、常に XSS を防げるわけではない。 そうした箇所を危険なコンテキストと呼ぶ。 危険なコンテキストには次のものが含まれる。
<script>Directly in a script</script>
<!-- Inside an HTML comment -->
<style>Directly in CSS</style>
<div ToDefineAnAttribute=test />
<ToDefineATag href="/test" />
その他に注意すべき箇所を挙げる。
- コールバック関数
- コード内で URL が扱われる箇所(
CSS { background-url : "javascript:alert(xss)"; }など) - すべての JavaScript イベントハンドラ(
onclick()、onerror()、onmouseover()) eval()、setInterval()、setTimeout()のような安全でない JS の関数
危険なコンテキストに変数を置いてはならない。 出力エンコーディングを施しても、XSS 攻撃を完全には防げないからである。
HTML のサニタイズ
利用者が HTML を書く必要がある場合、開発者は WYSIWYG エディタの中でコンテンツのスタイルや構造を利用者に変更させることがある。 この場合、出力エンコーディングは XSS を防ぐが、アプリケーションの意図した機能を壊してしまう。 スタイルが描画されなくなる。 こうした場合には HTML サニタイズを使うべきである。
HTML サニタイズは変数から危険な HTML を取り除き、安全な HTML 文字列を返す。 OWASP は HTML サニタイズに DOMPurify を推奨する。
let clean = DOMPurify.sanitize(dirty);
さらに考慮すべき点がある。
- コンテンツをサニタイズしたあとに変更を加えると、その安全対策は容易に無効になる。
- サニタイズしたコンテンツをライブラリに渡して使う場合は、そのライブラリが文字列を何らかの形で変化させないことを確認する。そうでなければ、やはり安全対策は無効になる。
- DOMPurify や他の HTML サニタイズライブラリには定期的にパッチを当てなければならない。ブラウザの挙動は変わり、回避手法は定期的に発見されている。
安全なシンク
セキュリティの専門家はしばしばソースとシンクという言葉で語る。 川を汚染すれば、それは下流のどこかへ流れていく。 コンピュータセキュリティでも同じである。 XSS のシンクとは、変数が Web ページに置かれる箇所を指す。
幸い、変数を置けるシンクの多くは安全である。
それらのシンクは変数をテキストとして扱い、決して実行しないからである。
innerHTML のような安全でないシンクへの参照を取り除き、代わりに textContent や value を使うようにコードを整えるとよい。
elem.textContent = dangerVariable;
elem.insertAdjacentText(dangerVariable);
elem.className = dangerVariable;
elem.setAttribute(safeName, dangerVariable);
formfield.value = dangerVariable;
document.createTextNode(dangerVariable);
document.createElement(dangerVariable);
elem.innerHTML = DOMPurify.sanitize(dangerVar);
安全な HTML 属性には次のものが含まれる。
align、alink、alt、bgcolor、border、cellpadding、cellspacing、class、color、cols、colspan、coords、dir、face、height、hspace、ismap、lang、marginheight、marginwidth、multiple、nohref、noresize、noshade、nowrap、ref、rel、rev、rows、rowspan、scrolling、shape、span、summary、tabindex、title、usemap、valign、value、vlink、vspace、width。
上に挙がっていない属性については、JavaScript のコードが値として与えられたときにそれが実行されないことを確認する。
その他の対策
フレームワークによる保護、出力エンコーディング、HTML サニタイズが、アプリケーションにとって最良の保護を与える。 OWASP はこれらをあらゆる状況で推奨する。
上記に加えて、次の対策の採用を検討する。
- Cookie の属性。JavaScript やブラウザが Cookie とどう相互作用できるかを変える。Cookie の属性は XSS 攻撃の影響を限定しようとするものであり、悪意あるコンテンツの実行を防ぐわけでも、脆弱性の根本原因に対処するわけでもない。
- Content Security Policy。コンテンツの読み込みを防ぐ許可リストである。実装で誤りを犯しやすいので、主要な防御機構にすべきではない。CSP は防御の追加の層として使い、Content Security Policy も参照する。
- Trusted Types。Chromium 系のブラウザでは、
Content-Security-Policy: require-trusted-types-for 'script'を付与して Trusted Types を有効にする。これにより DOM XSS のシンク(innerHTML、outerHTML、document.write、script.srcなど)が素の文字列を拒否するようになり、すべての代入が検証済みのポリシーを経由することを強制される。DOM XSS を緩和するのではなく、その一群を消し去る数少ない対策の一つである。レガシーなコード経路のために、サニタイザ(DOMPurify など)へ委譲する既定ポリシーと組み合わせる。 - Web アプリケーションファイアウォール。既知の攻撃文字列を探して遮断する。WAF は信頼できるものではなく、新しい回避手法が定期的に発見されている。また WAF は XSS 脆弱性の根本原因に対処しない。加えて、クライアント側だけで完結する種類の XSS 脆弱性を WAF は見落とす。XSS の防止、とくに DOM ベース XSS の防止に WAF は推奨されない。
XSS 防止規則のまとめ
以下の HTML の断片は、さまざまなコンテキストで信頼できないデータを安全に描画する方法を示す。
データ型:文字列
コンテキスト:HTML の本体
コード:<span>UNTRUSTED DATA </span>
防御の例:HTML エンティティエンコーディング(規則 #1)
データ型:文字列
コンテキスト:安全な HTML 属性
コード:<input type="text" name="fname" value="UNTRUSTED DATA ">
防御の例:積極的な HTML エンティティエンコーディング(規則 #2)。信頼できないデータは安全な属性の一覧(後述)にあるものにのみ置く。background、ID、name のような安全でない属性は厳密に検証する。
データ型:文字列
コンテキスト:GET パラメータ
コード:<a href="/site/search?value=UNTRUSTED DATA ">clickme</a>
防御の例:URL エンコーディング(規則 #5)
データ型:文字列
コンテキスト:SRC 属性または HREF 属性における信頼できない URL
コード:<a href="UNTRUSTED URL ">clickme</a> <iframe src="UNTRUSTED URL " />
防御の例:入力の正規化、URL の検証、安全な URL であることの確認、http と HTTPS の URL のみを許可リストに入れる(新しいウィンドウを開くために JavaScript プロトコルを使うことを避ける)、属性エンコーダ
データ型:文字列
コンテキスト:CSS の値
コード:HTML <div style="width: UNTRUSTED DATA ;">Selection</div>
防御の例:厳密な構造の検証(規則 #4)、CSS の 16 進エンコーディング、CSS 機能の適切な設計
データ型:文字列
コンテキスト:JavaScript の変数
コード:<script>var currentValue='UNTRUSTED DATA ';</script> <script>someFunction('UNTRUSTED DATA ');</script>
防御の例:JavaScript の変数が引用符で囲まれていることを確認する。JavaScript の 16 進エンコーディング、JavaScript の Unicode エンコーディング。バックスラッシュによるエンコード(\"、\'、\\)は避ける。
データ型:HTML
コンテキスト:HTML の本体
コード:<div>UNTRUSTED HTML</div>
防御の例:HTML の検証(JSoup、AntiSamy、HTML Sanitizer など)
データ型:文字列
コンテキスト:DOM XSS
コード:<script>document.write("UNTRUSTED INPUT: " + document.location.hash );<script/>
防御の例:DOM ベース XSS の防止
出力エンコーディング規則のまとめ
出力エンコーディングの目的は(クロスサイトスクリプティングとの関係において)、信頼できない入力を安全な形へ変換し、その入力がブラウザでコードとして実行されることなく利用者にデータとして表示される状態にすることである。 以下は、クロスサイトスクリプティングを止めるために必要な、重要な出力エンコーディング方式の一覧である。
エンコーディングの種類:HTML エンティティ
機構:& を & に、< を < に、> を > に、" を " に、' を ' に変換する。
エンコーディングの種類:HTML 属性エンコーディング
機構:空白を含むすべての文字を HTML エンティティの &#xHH; 形式でエンコードする。HH はその文字の Unicode における 16 進値を表す。たとえば A は A になる。英数字(A から Z、a から z、0 から 9)はエンコードせずに残す。
エンコーディングの種類:URL エンコーディング 機構:W3C の仕様で定められた標準のパーセントエンコーディングを用いてパラメータ値をエンコードする。URL 全体や URL のパス部分ではなく、パラメータ値だけをエンコードするよう注意する。
エンコーディングの種類:JavaScript エンコーディング
機構:すべての文字を Unicode の \uXXXX 形式でエンコードする。XXXX は Unicode コードポイントの 16 進表記を表す。たとえば A は \u0041 になる。英数字(A から Z、a から z、0 から 9)はエンコードせずに残す。
エンコーディングの種類:CSS の 16 進エンコーディング
機構:CSS のエンコーディングは \XX と \XXXXXX の両形式に対応する。適切にエンコードするには、次のいずれかを検討する。(a) CSS のエンコードのあとに空白を追加する(CSS のパーサはこれを無視する)。(b) 値をゼロ埋めして 6 文字の完全な CSS エンコード形式を使う。たとえば A は \41(短い形式)または \000041(完全な形式)になる。英数字(A から Z、a から z、0 から 9)はエンコードせずに残す。
よくあるアンチパターン。避けるべき効果のない方法
XSS への防御は難しい。 そのため、XSS を防ぐための近道を探す人がいる。
ここでは、古い投稿に頻出し、Stack Overflow などの開発者コミュニティにおける XSS 防御の現代の投稿でもなお解決策として引かれる、二つのよくあるアンチパターンを検討する。
Content-Security-Policy(CSP)ヘッダのみに依存する
まず明確にしておくと、適切に使われる CSP を我々は強く支持している。 XSS 防御の文脈において、CSP が最も効くのは次の場合である。
- 多層防御の一手法として使われるとき
- 一律の全社的な解決策として展開されるのではなく、アプリケーションごとに個別に調整されているとき
我々が反対するのは、企業全体に一律の CSP ポリシーを適用することである。 その方法には次の問題がある。
問題 1。ブラウザのバージョンが等しく CSP に対応しているという前提
通常、顧客のブラウザがすべて、一律の CSP ポリシーが用いる CSP の構成要素に対応しているという暗黙の前提が置かれる。
さらにこの前提は、User-Agent リクエストヘッダを明示的に検査して対応ブラウザかどうかを確かめ、そうでなければサイトの利用を拒否する、という処理を伴わずに置かれることが多い。
なぜか。
XSS 防止に頼っている CSP のレベル 2 やレベル 3 の構成要素に対応していない古いブラウザを使っている顧客を、ほとんどの企業は追い返したくないからである。
(統計的にはほぼすべてのブラウザが CSP レベル 1 のディレクティブに対応しているので、祖父が古い Windows 98 のノート PC を持ち出して古代の Internet Explorer でサイトにアクセスしてくることを心配しているのでなければ、CSP レベル 1 への対応は前提にしてよいだろう。)
問題 2。レガシーアプリケーションの対応の問題
企業全体に必須とされた一律の CSP レスポンスヘッダは、いくつかの Web アプリケーション、とくにレガシーなものを必ず壊す。 これは AppSec の指針に対する事業側の反発を招き、アプリケーションのコードが修正されるまで AppSec が免除や例外を発行する結果になるのが避けられない。 しかしこうしたセキュリティ上の例外は XSS に対する防具に亀裂を入れる。 その亀裂が一時的なものであっても、少なくとも評判という面では事業に影響しうる。
HTTP インターセプタに依存する
観測されるもう一つのよくあるアンチパターンは、検証や出力エンコーディングを何らかのインターセプタで扱おうとするものである。
たとえば org.springframework.web.servlet.HandlerInterceptor を実装する Spring のインターセプタや、javax.servlet.Filter を実装する JavaEE のサーブレットフィルタである。
ごく限られたアプリケーション(描画されるすべての入力リクエストが英数字のみであると検証する場合など)ではこれが成功しうるが、この方法は「出力エンコーディングはデータが描画される場所にできるだけ近い位置で行う」という XSS 防御の主要な原則に反する。
一般にこの方法では HTTP リクエストのクエリパラメータと POST パラメータは検査されるが、Cookie のデータのように描画されうる他のリクエストヘッダは検査されない。
よく見られるのは、ESAPI.validator().getValidSafeHTML() または ESAPI.encoder.canonicalize() を呼び、その結果に応じてエラーページへリダイレクトするか ESAPI.encoder().encodeForHTML() のような処理を呼ぶ、という実装である。
この方法がリクエストヘッダや URI 内の「追加のパス情報」といった汚染された入力をしばしば見落とすという事実は措いても、この方法は出力エンコーディングがまったくコンテキストを考慮していないという点を完全に無視している。
たとえば、あるクエリパラメータが HTML のコンテキスト(HTML タグの間)で描画されるのか、<script> タグの内側や JavaScript のイベントハンドラ属性のような JavaScript のコンテキストで描画されるのかを、サーブレットフィルタはどうやって知るのか。
知りようがない。
そして JavaScript のエンコーディングと HTML のエンコーディングは交換可能ではないので、XSS 攻撃に対して開いたままになる。
フィルタやインターセプタがアプリケーションの全体を把握し、とくに個々のリクエストで各パラメータがどう使われるのかを認識していない限り、ありうるすべての境界事例に対して成功することはできない。 そして我々は、この方法でそれが可能になることは今後もないと考える。 必要となる追加のコンテキストを与える設計はあまりにも複雑であり、それを試みれば、別の脆弱性(影響が XSS よりはるかに悪いものかもしれない)を偶発的に持ち込むことがほぼ避けられない。
この素朴な方法は、通常、次の四つの問題のうち少なくとも一つを抱える。
問題 1。特定のコンテキスト向けのエンコーディングが、すべての URI パスで十分とは限らない
一つの問題は、不適切なエンコーディングによって、アプリケーションの一部の URI パスで悪用可能な XSS が残ることである。
たとえば POST の lastname フォームパラメータが、通常は HTML タグの間に表示されるので HTML エンコーディングで十分だとする。
しかし lastname が実際には JavaScript ブロックの一部として描画される境界事例が一つか二つあり、そこでは HTML エンコーディングでは不十分で、XSS 攻撃に対して脆弱になる。
問題 2。インターセプタ方式は、不適切なエンコードや二重エンコードによって描画を壊しうる
二つめの問題は、アプリケーションが誤ったエンコードや二重エンコードを引き起こしうることである。
前の例で、開発者が lastname の JavaScript 描画に対して適切な出力エンコーディングを施していたとする。
しかしすでに HTML の出力エンコーディングも施されていれば、描画時に "O'Hara" という正当な姓が "O'Hara" のように出てしまう。
この二つめの場合は厳密にはセキュリティの問題ではないが、頻繁に起こればフィルタの使用に対する事業側の反発につながる。 その結果、事業側がフィルタの無効化や、特定のページやパラメータをフィルタ対象から外す方法を決めることがあり、それによってフィルタが提供していた XSS 防御が弱まる。
問題 3。インターセプタは DOM ベース XSS に効かない
三つめの問題は、DOM ベース XSS に効かないことである。 それに対処するには、HTTP レスポンスの一部として送られる JavaScript のコンテンツをすべてインターセプタやフィルタが走査し、汚染された出力を割り出し、それが DOM ベース XSS の影響を受けるかどうかを判断しなければならない。 これは端的に実用的ではない。
問題 4。レスポンスのデータがアプリケーションの外部に由来する場合、インターセプタは効かない
インターセプタの最後の問題は、内部の REST ベースの Web サービスや内部のデータベースなど、他の内部の情報源に由来するデータがアプリケーションのレスポンスに含まれる場合に、それを認識できないことである。 問題は、アプリケーションがそのデータを取得した時点で厳密に検証していない限り(一般に、許可リスト方式による厳密なデータ検証を行うのに十分なコンテキストをアプリケーションが持つのは、その時点だけである)、そのデータは常に汚染されているとみなすべきだという点にある。 ところが、汚染されたデータすべてに対する出力エンコーディングや厳密なデータ検証を、インターセプタ(Java のサーブレットフィルタなど)の HTTP レスポンス側で行おうとすると、その時点でアプリケーションのインターセプタは、利用した REST Web サービスや他のデータベースに由来する汚染データが存在することを知りようがない。 XSS 防御を提供しようとするレスポンス側のインターセプタで一般に採られる方法は、対応する「入力パラメータ」だけを汚染されたものとみなして出力エンコーディングや HTML サニタイズを施し、それ以外はすべて安全とみなすものである。 しかし、そうでない場合もあるのではないか。 内部の Web サービスや内部のデータベースはすべて「信頼できる」のでそのまま使えると想定されがちだが、アプリケーションの脅威モデリングを深く行ったうえでのことでなければ、これは非常に悪い想定である。
たとえば、顧客に月次の明細を見せるアプリケーションを開発しているとする。 そのアプリケーションは、利用者の氏名や住所などを取得するために、(そのアプリケーション自身の一部ではない)外部の内部データベースや REST Web サービスに問い合わせているとしよう。 しかしそのデータは、「信頼できる」と想定している別のアプリケーションに由来し、実際には住所関連の各フィールドに未報告の持続型 XSS 脆弱性があるとする。 さらに、自社の顧客サポート担当者が、明細についての問い合わせに応じるために顧客の詳細な明細を閲覧できるとしよう。 悪意ある顧客が住所のフィールドに XSS の爆弾を仕込み、明細について助けを求めて顧客サービスに電話する。 このような筋書きが実際に起きたなら、XSS を防ごうとするインターセプタはそれを完全に見落とし、結果は「1」や「XSS」や「pwn'd」を表示する警告ダイアログが出る程度よりはるかに悪いものになる。
まとめ
最後に一点。 インターセプタやフィルタを XSS 防御として展開することが XSS 攻撃に対して有用な方法であるなら、それはすべての商用の Web アプリケーションファイアウォール(WAF)に取り込まれ、OWASP がこのチートシートで推奨する方法になっていたはずではないか。
関連資料
XSS 攻撃のチートシート
以下の記事は、攻撃者が各種の XSS 脆弱性をどのように悪用するのかを説明している(本稿はそれを避ける助けとして作られた)。
XSS 脆弱性の解説
- XSS 脆弱性に関する OWASP の記事
XSS 脆弱性の種類についての議論
クロスサイトスクリプティング脆弱性のコードレビュー
クロスサイトスクリプティング脆弱性のテスト
- クロスサイトスクリプティング脆弱性のテストに関する OWASP Testing Guide の記事
- XSS Experimental Minimal Encoding Rules。XSS 攻撃を防ぐための、実験的な最小限のエンコード方針の例と指針を示している。