[PHP] 余計なHTMLタグや属性を消してくれるHTML Purifier | A Day In The Boy's Life

A Day In The Boy's Life

とあるエンジニアのとある1日のつぶやき。

HTMLタグをそのままユーザーに入力させ、それを正しい形で利用したいというケースは少ないかもしれませんが、ブログシステムやそれに似通った機能をユーザーに提供したい場合、その中から不正なHTMLタグをチェックするロジックを作ることは結構大変なことです。

こういったケースでは、HTML Purifier を使えば比較的容易に安全にHTMLタグを扱えるようになります。


OSSのブログシステム用プログラムとして有名なWordpress も、HTMLタグのフィルタとしてHTML Purifierを利用しているようです。

Wordpressを使ってなくても、例えば「高機能でカスタマイズも容易なWYSIWYGエディタ [CKEditor] 」で書いたような独自の入力フォーム用のブログシステムを作りたいといったケースでも適用できるかもしれません。



HTML Purifierの使い方


HTML Purifierのインストール方法は何種類か用意されており、PEARからインストールすることや、ソースプログラムをダウンロードして適当なディレクトリに保存すること、gitからインストールするというパターンなどがあります。

ここでは、簡単に導入できるPEARを使うパターンで書いています。


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

上記のようにインストールすると、PEARのディレクトリ内にHTML Purifierのプログラムが展開されます。

HTML Purifierの利用は、下記のように使います。


<?php

require_once 'HTMLPurifier.auto.php';

$config = HTMLPurifier_Config::createDefault();
$config->set('Core.Encoding', 'UTF-8');
$config->set('Core.Language', 'ja');
$config->set('HTML.AllowedElements', array('a', 'em', 'p', 'h2', 'span', 'br'));

echo $hp->purify($_POST['data'];


上記は、dataというパラメータで送られてきた値をHTML Purifierで処理しています。

基本的には、許可したいタグや動作をConfigオプションで指定し、処理させる変数に対して最後にpurify()メソッドを通すことで余計なタグを除去したりすることが出来ます。


HTML.AllowedElementsでは、入力を許可したいHTMLタグを指定します。

上記の場合はアンカータグや改行タグは許可しますが、記述していないタグは全て取り除かれます。

<h3>foo</h3><br />
<span>bar</span><br />
<div>hoge</div><br />


上記のようなタグをユーザーが入力した場合、下記のように変換されます。


foo<br />
<span>bar</span><br />
hoge<br />

h3やdivタグは許可するタグのリストに加えていないので全て除去されています。


HTML Purifierを応用して独自のフィルタを作る


使い方の基本系は、先に書いた例のようにConfigオプションを指定して、許可したいタグや属性を決めていきます。

指定できるオプション はかなり多いので、マニュアルも参照してみてください。

以下、よく使いそうなものから幾つかピックアップして解説します。


・不許可にしたいHTMLタグを指定する


HTML.AllowedElementsでは、ホワイトリスト方式で許可するHTMLタグを指定しましたが、HTML.ForbiddenElementsを使えば、その逆のブラックリスト方式で不許可にするHTMLタグを指定できます。

HTML.AllowedElementsを使わない場合でも、scriptタグやembedタグなど動作に問題がありそうなタグは一通り除去してくれたりします。

ここに許可するHTMLタグをリストアップするのが大変な場合は、こちらを使った方が楽に指定できます。


$config->set('HTML.ForbiddenElements', array('table', 'tr', 'td', 'tbody'));


・特定のドメインへのリンクを禁止する


問題のあるサイトへのリンクを作らせたくない場合は、下記のようにブラックリストのドメインを指定することができます。


$config->set('URI.HostBlacklist', array('example.com'));

処理例は、下記のようになります。


<a href="http://www.example.com">こちらへ</a><br />
<a href="http://www.example.co.jp">こちらへ</a>


上記のHTMLタグが入力された場合、禁止しているexample.comの方のアンカータグが無効化されます。

一方、example.co.jpドメインは禁止していないのでそのまま通ります。


<a>こちらへ</a><br />
<a href="http://www.example.co.jp">こちらへ</a>


・リンク(アンカータグの使用)を禁止する


こちらは、もう少し厳しくなりHTMLタグ内にアンカータグを使わせたくないという場合です。


$config->set('URI.Disable', true);


結果は、URI.HostBlacklistを使った場合と同様に、アンカータグが無効化されます。


・リンクのターゲットを限定する


アンカータグ内で指定できるtarget属性を限定させるということも出来ます。

下記の場合は、targetに「_self」と「_top」しか指定できません。


$config->set('Attr.AllowedFrameTargets', array('_self','_top'));

例えば、新しいウィンドウを開く「_blank」を指定するとその属性値が丸ごと削除されます。


<a href="http://www.example.com" target="_blank">こちらへ</a><br />
<a href="http://www.example.co.jp" target="_top">こちらへ</a>


以下のように、アンカータグは使えますがtarget属性が削除されています。


<a href="http://www.example.com">こちらへ</a><br />
<a href="http://www.example.co.jp">こちらへ</a>


・指定できるクラス名を限定する


例えば予め適用できるCSSがあり、そこに定義されているクラス以外を指定させたくないということも出来たりします。


$config->set('Attr.AllowedClasses', array('foo', 'test'));


上記の場合は、class属性にfooとtestしか指定できません。

これ以外のクラス名を指定した場合は、class属性から削除されます。


・要素と属性の組み合わせを限定する


より厳密に利用できる要素と属性の組み合わせを指定したい場合は、HTML.AllowedAttributesを利用します。


$config->set('HTML.AllowedAttributes', array('span.style', 'a.href', 'p.id' ,'div.class'));


上記の場合は、spanタグにはstyle属性を、aタグにはhref属性を、pタグにはid属性を、divタグにはclassタグを指定できますが、これ以外の組み合わせは利用できません。


<span class="foo" style="color:#FF0000">test</span>
<div class="bar">hoge</div>
<p class="bar">fuga</p>

上記のように入力された場合、HTML Purifierで処理後は下記のように変換されます。


<span style="color:#FF0000;">test</span>
<div class="bar">hoge</div>
<p>fuga</p>


spanとpタグの中ではclass属性は使えないので削除され、divタグは組み合わせが正しいのでclass属性を指定できています。



紹介できたのは本の一例なんですが、組み合わせ方ではかなり強力なHTMLタグのフィルタを書く事が出来るかと思います。

HTMLタグをそのままユーザーに入力させたいけど、セキュリティを考慮したら利用を一部制限したい、というようなケースにうまく使えるかもしれません。