HTML Purifierを試した

Webメールでは、受信したHTMLメールのタグを残して、クライアントサイドスクリプトJavaScriptなど)のみを除去するサニタイズ処理を行ないます。

このようなスクリプト除去処理は、Blog、掲示板などでも行なわれる、比較的ポピュラーなものであるため、それ用のライブラリがいくつか存在します*1

HTML Purifierもその一つです。PHP4/5で動作します。バージョン1.0.0のリリースは2006年9月ですから、比較的新しいものです。私は昨年末に初めて触ってみました(バージョン1.3.2です)。

これまでのものと比べて、非常によく出来ているなと思いましたので、日記に書いてみます。

HTML Purifierの概要

特徴としては、以下が挙げられると思います。

ユニコード対応

UTF-8のHTMLに対応(既存のライブラリの多くはlatin1を暗黙の前提としていました)。EUC-JPやSJISなど、他の文字コードも使用できます*2

HTMLを再構築

(当然ですが)HTMLをParseして分解する⇒フィルタ適用⇒HTML再構築という手順で処理します。

HTML言語仕様知識を持つ

HTMLやCSS言語仕様がライブラリに収められており、言語仕様に基づくフィルタ処理を行ないます。これがこのライブラリの最大の売りです。

  • タグの包含関係をチェック。
  • 属性値を分類し(bgcolor属性は%Color、width属性は%Length、src属性は%URIなど)、分類毎のチェックパターンで属性値をチェック。
  • style属性値もホワイトリストでチェック

HTML Purifierに関するより詳細な情報は、以下のページで得られます。

HTML Purifier - Filter your HTML the standards-compliant way!

処理の流れ

もう少しだけ詳細に、HTML Purifierの処理の流れを追っていきます。

前処理
  • HTMLのBODY内を抽出する。
  • CDATAを展開する。Big5(< > " & ')は文字参照表現。
  • Big5を除く文字参照UTF-8バイト列に展開する。
  • UTF-8として不正なバイト列を削除する。
  • non-SGML文字(制御文字類)を削除する。
Parse処理
  • PHP4では独自Parser、PHP5ではDOM関数を使う。
  • 開始・終了タグ、空タグ(空要素)、Textに分解する。
  • Text、属性値内のBig5文字参照を展開する。
フィルタリング
  • 許可するタグ以外(コメント含む)を削除。
  • 終了タグの追加などで、整形式(Well-formed)にする。
  • 包含関係が不正なタグを削除。
  • 許可する属性以外を削除。
  • 属性タイプに応じて属性値をチェック。不正な属性は削除。
HTML生成
  • 開始・終了タグ、空タグ、TextをHTMLに展開する。
  • Text、属性値内の< > " &を文字参照化する。

ベースとなる言語仕様

このライブラリで使用できるタグ、属性、CSSプロパティは、HTML Purifier Printer Smoketestに記述されています。基本的に、HTML4.01(Transitional)、CSS Level 1をベースにしているように思われます。

しかし、言語仕様で定義されていながら、このライブラリでは使用できないものもあります(SCRIPTタグやイベント系属性だけではありません)。例えば、以下です。

  • IFRAME、STYLE、OBJECT/APPLET、AREA、FORMタグ関連
  • テーブル類の背景色・幅指定属性
  • Aタグのtabindexやaccesskeyなどの属性
  • 背景画像指定のCSSプロパティ

このライブラリはページの一部分に、外部のHTMLを挿入するような使い方を前提としています。そのような使い方にそぐわないタグ、属性などを使用不可にしているという理由もあるようです。

また、一部の属性値やCSSプロパティ値に、必要以上に厳しい制限を掛けています。例えば、CSSで「MS明朝」のようなマルチバイト文字を含むフォント名は指定できません(/* foo */ 形式でのコメントなども使用できません)。

利点と欠点

既存ライブラリと比較しての、利点と欠点です。

利点:安全性が高い

真っ当な(かつ安全側に倒した)処理をしており、XSSの危険性は低いと思います。少なくとも私は攻略できませんでした。興味のある方は、デモページで試せます。

欠点:HTML記述の制約が多い

本質的な問題ですが、このライブラリは言語仕様準拠でないHTMLを許容しません(違反している要素を無視するだけで、HTML全体を拒絶するわけではありません)。世の中の多くのHTMLは、言語仕様に準拠しているとは思えませんので、何らかの影響が出ます。さらに、実際に適用されるフィルタは、言語仕様より厳しい基準のものです。

欠点:サーバリソースを消費する

既存のライブラリと比べて処理は複雑です。測定していませんが、消費するCPU、メモリ資源は相対的に多いと考えられます。処理時間も掛かるでしょう。

感想

CSSにさえもホワイトリストを適用しているのは驚きです。欠点もありますが、進んだ発想のライブラリだと思います。少なくとも、既存のPHPスクリプト除去用ライブラリや、巷のポータルサイトWebメールのフィルタよりも、はるかに良いです。

通常のページと、外部由来のHTMLを表示するページを分離する対策(ドメインや認証用トークンなどを分離し、XSS被害が波及しないようにする)が難しい場合には、使用を検討する価値があると思います。

*1:PHPのライブラリは、拙文「よりセキュアなWebサイト構築 - 4.7 JavaScriptの除去」にまとめています。HTML_Safe、kses、HTML Filter for PHPなどを試しましたが、いずれも安全とはいえないものでした。

*2:ライブラリへの入出力時にコード変換を行います。ライブラリの内部処理はUTF-8です。他の文字コードを使う場合、コード変換ロジックによっては、スクリプト挿入の危険性があるので要注意です(参考:UNICODE とサニタイジング回避テクニック)。コード変換にはiconvが使用されます。