PHP 5.4におけるhtmlspecialchars()の問題

PHP 5.4 beta 2が出ているところですが、ソースコード(html.c)を見ていて、XSS対策で使用される htmlspecialchars()の文字コード指定がPHP 5.3とは異なる動作をすることに気が付きました。

内部文字コード(mbstring.internal_encoding)が使用される以下のようなシチュエーションです。

<?php // test.php
echo htmlspecialchars("foo",ENT_QUOTES,"");

mbstring.internal_encoding="SJIS-win"と指定することにします。(推奨はできませんが、)外字対応(絵文字対応)でCP932を使用したい場合を想定しています。

  • PHP 5.3で実行
php53/sapi/cli/php -d mbstring.internal_encoding="SJIS-win" test.php

出力は、"foo"となります。 PHP 5.3では、"SJIS-win"はShift_JISと認識されます。

  • PHP 5.4で実行
php54/sapi/cli/php -d mbstring.internal_encoding="SJIS-win" test.php

以下のように警告が発生します。

Warning: htmlspecialchars(): charset `SJIS-win' not supported, assuming utf-8 in /home/rui/work/php/src/test.php on line 2
foo

この場合、SJIS-winが有効な文字コードとして認識されず、htmlspecialchars用の文字コードとしてデフォルトの文字コードUTF-8)が使用されてしまいます。上記の警告を無視して対策を講じない場合、XSS等に関するリスクを発生する可能性があります。

対策としては、やはりhtmlspecialchars()の第3引数に明示的に文字コードを指定するのが良いでしょう。日本語の文字コードとして有効なのは、Shift_JIS系では"Shift_JIS"、"SJIS"、"932"、EUC-JP系では"EUCJP","EUC-JP"です。

<?php
echo htmlspecialchars("foo",ENT_QUOTES,"SJIS");

なお、htmlspecialchars("foo",ENT_QUOTES,mb_internal_encoding()) のようなコードも存在するようですが、上記の設定ではSJIS-winとなってしまいデフォルト値(UTF-8)が使用されるため、使用できません。また、そもそも0x5c問題などを誘発しやすいSJIS系の文字コードを内部文字コードに使用するのは避けるべきと思います。

PHP 5.3系では文字コード指定を省略した場合、デフォルト値としてISO-8859-1が使用されていましたが、PHP 5.4系では UTF-8が使用されます。UTF-8を使用する場合は便利になりますが、しばらくはバージョンが混在して使用されるケースもありますので、やはり、明示的に文字コードを指定する方が良いでしょう。

(P.S.)
SJIS-win、CP932、eucJP-winについてPHP 5.3と動作が同じとなるようにパッチをコミットしておきました。