悪意あるコードの侵入を阻止するツール、HTML Purifier

 HTML Purifierは、HTMLの正当性を確保しクロスサイト・スクリプティングなどの悪意あるコードの侵入を阻止するツールだ。同プロジェクトのページPluginsには、現在、CodeIgniter、Drupal、MODx、Phorum、Joomla!、WordPress用のプラグイン類が用意されている。これを導入しておけば、利用者が、閲覧したブラウザーで実行される悪意あるコードをHTMLコンテンツに挿入することができなくなる。実際の動作をデモ・ページで確かめることもできる。

 HTML Purifierは、既知の悪意あるHTMLコードを探すブラックリスト方式ではなく、ホワイトリスト方式を採用している。したがって、正当なHTML文書を構成するすべてのパーツを明示的に指定する必要がある。どのようなコンテキストで何が許されるかは、Smoketestページで確認することができる。正当なHTMLとは何か、要素間の入れ子関係、各要素に付随するHTML属性として正当なものを明確に示すこともHTML Purifierの目的の一つなのだ。CSSにも対応しており、http://で始まるテキストを自動的にHTMLのhref要素に書き換えるといったことができる。HTML Purifierへの乗り換えを検討する場合は、各種HTML検証ツールと比較しているComparisonページが参考になるだろう。

 HTML PurifierはUbuntu、Fedora、openSUSE用のパッケージさえ提供しておらず、PEARを使ってインストールする。PEARによるインストールは高速で、最新版に移行するのも簡単(pear upgradeというコマンドを実行するだけ)。スクリプトにパスを指定する必要がないため、スクリプトにHTML Purifierを含めるのも容易だ。このほか、自分でソースからビルドできるようtarballが用意されている(tarballは、ドキュメントや依存するパッケージの収載の有無が異なる3種類が用意されている)。

 HTML PurifierをPEARを使ってインストールする場合は、まずphp-pearパッケージをインストールし、その後pearコマンドでHTML Purifierをインストールする。一例を下に示す。これはHTML Purifierを/usr/share/pear/HTMLPurifierにインストールする。

pear channel-discover htmlpurifier.org
pear install hp/HTMLPurifier

 筆者が試用したときは、この状態でHTML Purifierを使おうとするとエラーになった。Apacheのログ・ファイルを見ると、Cache.SerializerPathパスが存在しないとなっている。/usr/share/pear/HTMLPurifier/DefinitionCache/Serializerはコンテンツをキャッシュするためのパスで書き込み可能でなければならない。このエラーを回避するには、キャッシュしないようにするか(INSTALLファイルに詳しく説明されている)、/usrにディレクトリーを作って一時的なキャッシュとして使うか、あるいは/varに新しいディレクトリーを作ってここにキャッシュする。下に、最後の方法による場合の例を示す。

# mkdir -p /var/cache/HTMLPurifier
# chown apache /var/cache/HTMLPurifier
# chmod o-rwx /var/cache/HTMLPurifier
# ls -ld /var/cache/HTMLPurifier
drwxr-x--- 2 apache root 4096 2008-06-25 14:25 /var/cache/HTMLPurifier

 残念ながら、SerializerPathのデフォルト・パスはHTMLPurifier/ConfigSchema/schema.serの中に埋め込まれている。これは、テキストの冒頭にテキスト長を指定するタイプのファイルなので、人間が編集するにはあまり適していない。したがって、ご自分のPHPコードの構成オブジェクトを使ってパスを変更するか、もっといいのは、ご自分のWebサイト向けに構成オブジェクトを設定するPHP関数を作っておく。

 HTML Purifierを使う簡単な例を下に示す。このindex.phpファイルでは、同じページ上のフォームからサブミットされたHTMLコンテンツをクリーンアップする。ここでhtmlspecialcharsを呼び出しているのはセキュリティーのためではなく、単に利用者が入力したHTMLテキストをpre要素の中に入れ利用者に見せるためだ。

# cd /var/www/html
# mkdir HTMLPurifierTest
# chown ben.apache HTMLPurifierTest
# chmod +s HTMLPurifierTest
# su -l ben
$ cd /var/www/html
$ vi index.php
<?php

  require_once 'HTMLPurifier.auto.php';

  $config = HTMLPurifier_Config::createDefault();
  $config->set('Core', 'Encoding', 'ISO-8859-1');
  $config->set('HTML', 'TidyLevel', 'heavy' );
  $config->set('Cache', 'SerializerPath', '/var/cache/HTMLPurifier' );
  $purifier = new HTMLPurifier($config);

?>

<html>
<body>

<p>
Enter your nastiest HTML below!
</p>

     <form name="myfrom" action="index.php">
         <input type='text' name='query'></form>
     </td>

<br/>

<p>
This is the clean part of what you said...
</p>

<pre>
<?php
  $clean_html = $purifier->purify($query);
  print htmlspecialchars($clean_html);
?>
</pre>
</body>
</html>

 利用者が入力可能なHTML要素を明示的に制限したいときは、下に示すようにForbiddenElements構成ディレクティブを使う。この例では、入力されたHTMLからボールド、イタリック、フォーマット済みを表す各タグを取り除いている。逆に、AllowedElementsを使ってホワイトリストに使用可能な要素を明示的に指定してもよい。

  $config->set('HTML', 'ForbiddenElements', 'b,i,pre');

 HTML Purifierには、URIのフィルタリングとマングリング機能もあり、メインとなる検証の前後どちらのタイミングでも適用できる。HTML入力を検証する前にフィルターに通すことができるため、正当ではないURIをHTML Purifierが排除しないようなものに変更することができる。たとえば、画像などのメディア・ファイルへのリンクを許容する場合、その画像の一意識別子をそのままパスさせ、カスタムURIマングラーを使ってカスタムURIを実際の絶対的HTTP URLに変更する。

 URIフィルターの一つとしてホスト・ブラックリストがある。これは所定のホスト名をブロックする。ただし、利用の際は注意を要する。URLの一部がブラックリストの名前と一致するだけで拒否されるからだ。幸いなことに、ホスト・ブラックリスト・クラスのコードは短いため、所定の接尾辞で終わるURLだけを調べるようなクラスを簡単に定義することができる。こうしたURIフィルターが、実装を予定する機能としていくつかリストされている。

まとめ

 キャッシュ・ディレクトリーに関するインストールの問題は、おそらく、PEARの制約だろう。少なくとも、HTML Purifierは/usrの下にパスを作って動くのではなく、丁寧なエラー・メッセージを出して、一時的なキャッシュ・ドキュメントをどこに保存すべきかという問題を解決するよう使用者に求める方法を選んでいる。

 HTML Purifierにより、適正形式のHTMLコードではなく悪意あるHTMLコードをWebフォームに入力されるのを防ぐことができる。また、使用を許容するHTML要素などのホワイトリストやURIフィルタリングを使えば、フォームに不正なデータを直接入力するという悪戯を阻止することもできる。匿名でもポストを許すフォーラムを運営している場合なら、URIフィルタリングはフォーラム・スパムを防止するのにも大いに役立つ。たとえば、匿名でのポストでは自サイトへのリンクだけを許可し、ユーザー登録すれば外部のサイトへのリンクも使えるようにすることができる。

Ben Martin 10年以上にわたってファイルシステムを研究。博士課程を修了し、現在、libferris、ファイルシステム、検索ソリューションを中心にコンサルティングをしている。

Linux.com 原文(2008年7月8日)