そもそも
基本的に内部的な文字コードは(ソースファイルもDBもその他の設定も)全てUTF-8にしておき、POSTやGETなどで入力値を受け取る時には入力値をUTF-8に変換してから使うのが良いと思うし、そうすれば漏れがない限りはhtmlSpecialChars()等で出力時に不正文字コードを気にする必要はない。
しかし
現実的にはPOST/GETで受け取る以外にも下記の様に様々な方法でデータが入ってくることもある。
- URLの一部から取り出す
- テキストファイルから読み込む
- APIで取得する(例えば、Amazon Production Advertising APIのレスポンスXMLにUTF-8でない文字列が入ってることがあった...)
- Cookieから取り出す
- アップロードされたファイルのファイル名(PHPのバージョンによっては入力値扱いとして変換される。参考:文字エンコーディングの妥当性確認(バリデーション)について - t_komuraの日記)
なので、やはり出力時にも不正な文字コードは弾いた方がよい。
で、調べてみた
2009年の後半にいくつかのブログでこのことが論じられていた。(そういえば読んだ記憶が...。詳細は下の方の参考リンクを参照。)
中でも「htmlspecialchars - 第45回PHP勉強会@関東 :: handsOut.jp」が簡潔でわかりやすかった。
PHP5.2.5以降を使い、htmlspecialchars()の第3引数にUTF-8を指定するのが一番堅実のようだ。
蛇足だが、もしUTF-8以外でWebページを入出力したい場合は、mb_http_output()とob_start()を使って出力完了後に文字コードをSJIS-WINなどに変換させるようにしておいて、そこにUTF-8で出力するのが良いと思う。(処理量が多いとオーバーヘッドが気になる?)
以外な落とし穴が
しかし、htmlspecialchars()の文字コード検出には思わぬ落とし穴があった。
htmlspecialchars()は不正な文字コードを見つけても、PHPの設定でdisplay_errorsがfalseの場合しかWarningを出さない。
- display_errors = true → Warningを画面にもログにも出さない
- display_errors = false → Warningをログにも出す(画面には出さない)
これは意図的なものなので、修正されない可能性が高そう。(参考:display_errorsが謎の副作用を持っている箇所について - muddy brown thang)
また、@htmlspecialchars()に@(アットマーク)を付けてもログへのエラー出力を抑制できなかった。
Warningを出さないようにする対策としては、htmlspecialchars()の手前で強制的に文字コードを変換するという手がある。
$str = mb_convert_encoding($str, 'UTF-8', 'UTF-8');現在のサーバ性能とPHPの速さなら、こんな富豪的な重複処理もあまり問題にならない環境が多い。
echo htmlSpecialCahrs($str, ENT_QUOTES, 'UTF-8');
mb_convert_encoding()とhtmlSpecialChars()の検出基準(?)が違うというようなことをどこかで読んだ気がするんで、その場合はこれでもWarningが出るのだろう。
でも、どうせmb_convert_encoding()するのなら、そもそもhtmlSpecialChars()での文字コード指定は要らないか。
ただし、本道としては、せっかく出してくれるWarningを手がかりにして、該当箇所で適切な文字コード変換をするようコードを修正するのが筋だろう。
PHPのWarningはset_error_handlerで捕まえられる。
もう1つ
「htmlspecialchars/htmlentitiesはBMP外の文字を正しく扱えない - [php] - 徳丸浩の日記」という落とし穴もあるようだが、これはPHPの5.3.2でFixした。(5.2系は?)
また、(線文字Bは置いておいて)一部の漢字を「使えません。仕様です。」で済ませられるなら、こちらの問題は黙殺できそうだ。
参考
- htmlspecialchars()関数の第3引数(文字エンコード)について | PHP逆引きレシピ オフィシャルサポート
- htmlspecialcharsは不正な文字エンコーディングをどこまでチェックするか - [php][security][xss] - 徳丸浩の日記
- htmlspecialcharsに関する残念なお知らせ - 岩本隆史の日記帳
(この岩本さんのバグレポートはmoriyoshiさんが対応してくれたみたい。ChangeLogを見るとPHPの5.2.12で#49785がFixされたみたいだけど、5.3系にも取り込まれてるのかな?)
0 件のコメント:
コメントを投稿