はじめに
今回はXSSの脆弱性をチェックするPerlスクリプトを作成したいと思います。すべてのXSSによる脆弱性が回避できるわけではありませんが、テストコード作成のヒントになれば幸いです。
対象読者
Webアプリケーション開発者で、XSSのテストケースを作成したい方。
必要な環境
Perl 5.8以上が動作する環境。基本動作の確認はMac OS Xを利用しました。次のPerlモジュールを利用するので、あらかじめインストールしておいてください。
またCGIを使用するので、ApacheなどのCGIが実行できるWebサーバを用意してください。
解説内容
ソースコード解説
まず最初にソースコードの解説をします。
- xss.pl
- xss2.pl
- xss.cgi
- xss2.cgi
「xss.cgi」と「xss2.cgi」はCGIが実行可能な場所へ設置してください。「xss.pl」と「xss2.pl」は任意の場所でOKです。本稿ではxss.cgiとxss2.cgiは「http://localhost/cgi-bin/」以下に設置している前提で進めます。
利用する組み合わせとしては、
- xss.pl+xss.cgi
- xss2.pl+xss.cgi+xss2.cgi
になります。順番に解説をしていきます。
単純なXSSのチェック
まずは、1番目のxss.plとxss.cgiを利用した簡単なスクリプトを見ます。処理の流れとしては次のようになります。
- フォームがあるHTMLページを取得します。
- HTMLページの中からformタグに囲まれたすべてを抜き出します(複数個のformタグがあればそれぞれを取得します)。
- formタグの中からinput、select、textareaのname属性をそれぞれWeb::Scraperモジュールを使って抽出します。
- 上記で取得したname属性を利用して悪意あるスクリプトの例
<code><script>alert("XSS");</script></code>
をクエリー文字列にのせて送信します。 - 送信した後のページを取得して、スクリプトがそのまま残っているかどうかをチェックします。スクリプトがそのまま残っているということは、スクリプトが実行される可能性があるということで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をもう少し汎用的にしてみたいと思います。
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サービスが少しでもセキュアな環境になるよう、その参考になれば幸いです。