Kentaro Kuribayashi's blog

Software Engineering, Management, Books, and Daily Journal.

HTML::StripScriptsでXSS対策をする

先日公開した「はて☆すたアンケート」にて、アンケートの説明文をはてな記法で書けるよう、機能追加を行った。その際、Template::Plugin::Hatenaを用いた。これは、はてな記法パーサであるText::Hatena(正確には、そのヴァージョン0.16以下)を、Template::Toolkitのプラグインとして使えるようにしたものである。
はてな記法は、それ自体で全ての文書構造を表現できる、あるいは、はてなダイアリのシステム自体は、はてな記法のみしか許容しないというものではなく、たとえば画像を貼る際には、普通にimg要素を書く必要があるし、また、その他の要素についても、記法が用意されていないものについては、「はてなダイアリーのヘルプ - はてなダイアリー利用可能タグ」に掲載されているものに限り、自分でタグを書くことができる。これは自由度を高める反面で、XSSを誘発し得る潜在的なリスクがある。であればこそ、「はてなダイアリーのヘルプ - はてなダイアリーXSS対策」に見られるような対策が重ねられてきたわけだ。
はて☆すたアンケート」におけるはてな記法の利用、および、それに伴う一部のタグ利用の開放に際して、当然、記法パーサの役割を担うのみであるText::Hatenaを利用するだけでは、上述のようなセキュリティ上のリスクをまったく考慮しないことになってしまう。つまり、Text::Hatenaによりはてな記法を変換した上で、ユーザ入力によるHTML断片の中に含まれ得る危険な要素を排除する必要がある。この取り扱いは非常に困難を極めるため、よくテストされた配慮を必要とする。
その問題に処するに際して、今回はHTML::StripScriptsを用いた。これは、ホワイトリストに基づいた要素や属性のフィルタリングを行うとともに、セキュリティ上の問題を誘発し得るスクリプト断片を除去することを目的としたモジュールである。スクリプト断片は、script要素やイヴェントハンドラ等、様々な場所で現れ得るが、それらを適切に除去してくれることになっている。また、その際に、以下に挙げる細かいオプションを指定することができる。

  • 入力されたHTML断片のコンテキストに応じて、許容する要素をあらかじめ用意(HTML全体/body要素中の、通常現れても問題ないと考えられる要素/インライン要素のみ/タグを一切許容しない等)
  • 指定したタグを許容しない/指定したタグ以外を許容しない
  • href、src属性を許容するかどうかを指定可能
  • 相対URLやmailtoスキームによるリンクを許容するかどうかを指定可能
  • 柔軟なカスタムルールの追加

コンテキストに応じて許容され得る要素の具体的な内容については、モジュールのソースを参照されたい。
以上に見たとおり、使い勝手が非常に良いモジュールであり、また、肝心のスクリプト除去については、通常のテストとともに、XSS (Cross Site Scripting) Cheat Sheetに挙げられたリストについても、その全てについてテストを行い、危険箇所が除去されることが確認されており、そのことによって全ての状況において安全とはもちろんいえないのだろうが、よくテストされたものであるように思われる。
このモジュールを「はて☆すたアンケート」のようなウェブアプリケーションで用いるに際しては、Template::Toolkitのプラグインとして利用するのがベストな方法だろう。それはまだ用意されていないようであったので、HTML::StripScriptsの、HTML::Parserを用いたヴァージョンであるHTML::StripScripts::Parserを利用して、Template::Plugin::StripScriptsとして、プラグイン化を行った。以下のようにして、テンプレート内で使用する。各オプションの詳細については、依存モジュールのドキュメントにあたられたい。

[% USE StripScripts %]
[% FILTER stripscripts Context             => 'Document',
                       BanList             => ['br' 'img'],
                       BanAllBut           => ['p' 'div' 'span'],
                       AllowSrc            => 1,
                       AllowHref           => 1,
                       AllowRelURL         => 0,
                       AllowMailto         => 0,
                       EscapeFiltered      => 0,
                       Rules               => { See the POD of HTML::StripScripts },
                       ParserOptions       => {
                           strict_names    => 1,
                           strict_comments => 1,
                       },
%]

   ... HTML which can cause XSS ...

[% END %]

or

[% myhtml | stripscripts options_like_above %]

上述した、「はて☆すたアンケート」におけるはてな記法、および、一部のユーザー入力によるタグの利用については、以下のような形でフィルタをつなげることでそれを可能とした。

[% myhtml | hatena | stripscripts options_like_above %]

ここでは、HTML::StripScriptsのHTMLの利用によるXSS対策について述べたが、それが完全にその脆弱性を排除し得ることを意味するわけでは、もちろんない。実際の利用に際しては、よくよく検討されたいと思う。