読者です 読者をやめる 読者になる 読者になる

PHP で h() を書くなら一緒に echo しよう。

名前が長い関数 htmlspecialchars() を h() と書けるようにすると便利、という話がある。

htmlspecialchars() なんて長い名前は絶対忘れるし、ENT_QUOTESだのUTF-8だのも書き忘れるに決まっている。

h() は CakePHP でも使われていて、いいと思う。いいと思うんだけど、もうちょっといける。

<?php 
/** 改善前のエスケープ関数 */
function h($str)
{
    return htmlspecialchars($str, ENT_QUOTES, 'UTF-8')
}

このようにエスケープした結果をreturnで返すと、エスケープと出力を離して書けてしまう。

$name = h($name);
:
// 長い長い処理の後に
:
<?php echo $name; // たしかエスケープ済だったかな ?>
<?php echo h($address); // こっちはエスケープしてなかったかも ?>

これだとぜったいエスケープし忘れる。もしくは二重にエスケープする。

エスケープが常に出力の直前になるように h() の中で echo も実行するといい。エスケープしていない(echoが直に書かれた)箇所を目立たせる結果にもなる。

<?php 
/**
 * HTMLの特殊文字をエスケープして結果を出力します。
 */
function h($str)
{
    echo htmlspecialchars($str, ENT_QUOTES, 'UTF-8');
}

次のように使う。

<?php h($name); ?>
<?php h($address); ?>

htmlspecialchars() を実行した文字列に対して更に何か加工するのは例外的なケースだが、よくあるのは改行を<br/>にする処理で、これは hbr() として定義する。

<?php 
/**
 * HTMLの特殊文字をエスケープして改行の前にbrタグを追加し、結果を出力します。
 */
function hbr($str)
{
    echo nl2br(htmlspecialchars($str, ENT_QUOTES, 'UTF-8'));
}

それ以外は頑張って htmlspecialchars() と書く、と言いたいけど、ENT_QUOTES、'UTF-8'を忘れそうなので、やはり関数を定義しておいたほうが安心かも。h() を使いたくなるように、名前は長めにする。

<?php 
/**
 * HTMLの特殊文字をエスケープして結果を返します。通常は h() を使います。
 */
function htmlescape($str)
{
    return htmlspecialchars($str, ENT_QUOTES, 'UTF-8');
}

配列をまとめてエスケープといった話はすっぱり忘れる。やるなら htmlescapeArray(array $array) を定義して、処理内容を明確にする。

参考

「出力の直前に」「例外なく」「エスケープしていることがわかるように」

セキュリティ云々以前に、プログラムの開発方法論として、できるだけ局所的な視点でコードの正当性を確認できるように書くのが、近代プログラミングの基本だ。つまり、このコード断片だけ見て、問題がないとわかるように書くべきである。
:
たとえば、echo を全面的に使用禁止として次のように書くのもよいだろう。

高木浩光@自宅の日記 - プログラミング解説書籍の脆弱性をどうするか, 「サニタイズ言うなキャンペーン」とは何か, ASPとかJSPとかPHPとかERBとか、逆だ..

PHP で何かを出力する際には,それが人の目に触れる形で表示されるかどうかにかかわらず,出力の直前に htmlspecialchars() を必ず通すようにしましょう。

セキュリティ上の配慮

以下の文字列をサニタイズする。サニタイズするタイミングは、データ受付時ではなく、HTML出力直前に例外なく行うこと。

セキュリティ指針/クロスサイトスクリプティング - ゼンド・ジャパン株式会社 技術情報コンテンツ

はまちちゃん先生の記事に触発されて書きました。