Shoeisha Technology Media

CodeZine(コードジン)

特集ページ一覧

Perlを使って脆弱性を検証する

PerlスクリプトでXSSの脆弱性をチェックする

  • ブックマーク
  • LINEで送る
  • このエントリーをはてなブックマークに追加
2007/09/20 14:00

HTMLページのクロスサイトスクリプティング(XSS)脆弱性を検出するPerlスクリプトを実装します。Webアプリケーション開発で、テストケースを作成している人の参考になるかと思います。

はじめに

 今回はXSSの脆弱性をチェックするPerlスクリプトを作成したいと思います。すべてのXSSによる脆弱性が回避できるわけではありませんが、テストコード作成のヒントになれば幸いです。

対象読者

 Webアプリケーション開発者で、XSSのテストケースを作成したい方。

必要な環境

 Perl 5.8以上が動作する環境。基本動作の確認はMac OS Xを利用しました。次のPerlモジュールを利用するので、あらかじめインストールしておいてください。

 またCGIを使用するので、ApacheなどのCGIが実行できるWebサーバを用意してください。

解説内容

ソースコード解説

 まず最初にソースコードの解説をします。

  • xss.pl
  • 簡単なXSSの脆弱性を調べるスクリプトです。
  • xss2.pl
  • xss.plに汎用性を持たせたより実用的なXSSの脆弱性を調べるスクリプトです。
  • xss.cgi
  • GETでリクエストすると入力フォームを表示し、POSTでリクエストするとクエリー文字列に従ってページを表示するCGIです。
  • xss2.cgi
  • クエリー文字列にtextとあった場合、Pタグで囲まれたテキストにXSSを引き起こすスクリプトを挿入します。
    クエリー文字列にattrとあった場合、Aタグのhref属性にXSSを引き起こすスクリプトを挿入します。
    何も指定がない場合は上記のスクリプトをエスケープして挿入します。

 「xss.cgi」と「xss2.cgi」はCGIが実行可能な場所へ設置してください。「xss.pl」と「xss2.pl」は任意の場所でOKです。本稿ではxss.cgiとxss2.cgiは「http://localhost/cgi-bin/」以下に設置している前提で進めます。

 利用する組み合わせとしては、

  1. xss.pl+xss.cgi
  2. xss2.pl+xss.cgi+xss2.cgi

 になります。順番に解説をしていきます。

単純なXSSのチェック

 まずは、1番目のxss.plとxss.cgiを利用した簡単なスクリプトを見ます。処理の流れとしては次のようになります。

  1. フォームがあるHTMLページを取得します。
  2. HTMLページの中からformタグに囲まれたすべてを抜き出します(複数個のformタグがあればそれぞれを取得します)。
  3. formタグの中からinput、select、textareaのname属性をそれぞれWeb::Scraperモジュールを使って抽出します。
  4. 上記で取得したname属性を利用して悪意あるスクリプトの例<code><script>alert("XSS");</script></code>をクエリー文字列にのせて送信します。
  5. 送信した後のページを取得して、スクリプトがそのまま残っているかどうかをチェックします。スクリプトがそのまま残っているということは、スクリプトが実行される可能性があるということでNGとします。スクリプトが残っていなければエスケープされたと見なしてOKとします。

 OKかNGかのチェックをする際には、Test::Simpleモジュールのokメソッドが手っ取り早いです。proveコマンドを使うことでレポートがまとまります。実行方法は<code>perl xss.pl</code>ではなく、次のように行います(もちろん<code>perl xss.pl</code>でも構いません)。

$ prove xss.pl
xss....
#   Failed test 'p1'
#   in xss.pl at line 41.
xss....ok 2/0                                                                
xss....NOK 3#   Failed test 'p3'                                             
#   in xss.pl at line 41.

#   Failed test 'mode'
#   in xss.pl at line 41.
# Looks like you failed 3 tests of 6.
xss....dubious                                                               
        Test returned status 3 (wstat 768, 0x300)
DIED. FAILED tests 1, 3, 6
        Failed 3/6 tests, 50.00% okay
Failed Test Stat Wstat Total Fail  List of Failed
---------------------------------------------------------------------
xss.pl         3   768     6    3  1 3 6
Failed 1/1 test scripts. 3/6 subtests failed.
Files=1, Tests=6,  1 wallclock secs (0.20 cusr + 0.03 csys = 0.23 CPU)
Failed 1/1 test programs. 3/6 subtests failed.

 3件のNGが発生したことが分かります。後はFailしたテストケースを解消していくことでXSSの脆弱性から回避できることでしょう。

 Template::Toolkitにはフィルタ機能が備わっていて、HTMLをエスケープしてくれる機能があります。|のあとにhtmlと記述するだけです。この記述すればエスケープして出力してくれますのでXSS対策に重宝します。

[% value | html %]

 xss.cgiのget_templateメソッドに記述されたTemplate Toolkitのディレクティブの部分すべてにhtmlフィルタを記述することでテストケースがすべてokになると思います。試してみてください。

$ prove xss.pl
xss....ok                                                                    
All tests successful.
Files=1, Tests=6,  1 wallclock secs (0.20 cusr + 0.03 csys = 0.23 CPU)

 これでXSSの脆弱性のリスクを回避することができました。

より汎用的なスクリプト

 次に、2番目のxss2.plとxss.cgiとxss2.cgiを利用した場合を見ます。

 昨今のWebサービスを見ると入力フォームの直後に結果が表示されるケースは稀になってきました。例えばブログツールのように管理画面で投稿した後に別のURLでその結果を参照することの方が多いと思います。そこでxss.plをもう少し汎用的にしてみたいと思います。

お断り
 通常であれば入力フォームからデータを送信した後、DBへデータを格納し、再度DBからデータを取得してデータを表示する、といった流れになると思います。しかし、このような実装は本稿のテーマとは離れる部分ですので、今回はデータを送信する部分をxss.cgiで、データを表示する部分をxss2.cgiで代用し、DBに格納する部分は省略しています。従ってxss.cgiとxss2.cgiとの整合性が取れていませんがご容赦ください。

 xss.plでは、scrape_and_checkメソッドでform部分のスクレイピングとデータの送信と結果のチェックを同時に行っていました。この部分は分離したいところです。結果のチェック部分を別メソッドとして定義することで役割を分担しました。

 さらにxss.plではデータを直接ソースコードに記述していました。これだとチェックしたいサイトが増えるたびにソースコードに手を入れる必要が出てくるので少し厄介です。そこで、Test::Baseモジュールを利用することでテストデータとソースコードを分離させて汎用性を実現しました。

 Test::Baseではテストデータ部分は、__END__以降に記述します。===がデータブロックの区切りを意味し、---がデータのセクションの区切りを意味します。そしてfilter機能を利用することでテストデータ部分をyamlで記述することができます。xss2.plではyamlを使ってテストデータを生成しました。

__END__

=== XSS Data
--- input
form_url: http://localhost/cgi-bin/xss.cgi
check_urls:
    - http://localhost/cgi-bin/xss2.cgi
    - http://localhost/cgi-bin/xss2.cgi?text=1
    - http://localhost/cgi-bin/xss2.cgi?attr=1
script: <script>alert("XSS");</script>

まとめ

 XSSのテストを実施したいページが複数ある場合はテストデータ部分に追加すると良いと思います。またログインが必要なページについては、get_form_htmlメソッドを拡張することで対処可能になるのではないでしょうか。

 本稿のサンプルスクリプトですべてのXSSを検知できるわけではありませんし、誤報が発生する可能性もあります。しかし、1つでも多くの XSS による脆弱性を取り除きたいのも事実です。皆さんが運営または開発されるWebサービスが少しでもセキュアな環境になるよう、その参考になれば幸いです。

参考資料



  • ブックマーク
  • LINEで送る
  • このエントリーをはてなブックマークに追加

著者プロフィール

バックナンバー

連載:Perl Tips
All contents copyright © 2005-2019 Shoeisha Co., Ltd. All rights reserved. ver.1.5