第四引数double_encodeでhtmlspecialcharsの二重エスケープ防止

製作 プログラム

2016/08/23(火)

htmlspecialcharsを二重に使ってしまうと…

phpのhtmlspecialchars()という関数を使うとクロスサイトスクリプティング(XSS)対策において必須の特殊文字のエスケープ処理を簡単に行うことが出来ます。

htmlspecialchars()関数や以前の記事で書いたjavascriptで行われているエスケープ処理は特殊文字を文字参照というものに変換しています。

具体的には記号&は『&amp;』という文字参照で表され、記号<は『&lt;』という文字参照で表すことができます。

試してみよう

自身のhtml文に文字参照『&amp;』や『&lt;』をコピペしてブラウザで見てみてください。html上は文字参照なのにブラウザ上は記号になっていることが確認できます。

ただこのhtmlspecialchars()関数を同じ文字列に対して複数回使用するとあまりうれしくない出力結果になります。

php -0回目


<タカ&トシ>

php -1回目


<タカ&トシ>

php -2回目


&lt;タカ&amp;トシ&gt;

php -3回目


&amp;lt;タカ&amp;amp;トシ&amp;gt;

と、こんな風に回数を重ねるごとにタカとトシの間が広がっていき、ごちゃごちゃした記号が増えていっているのがわかると思います。

これをもう少しピンポイントで見てみましょう。

0回目


&

1回目


&

2回目


&amp;

htmlspecialchars()は文字列「&」に記号&が現れたらそれを文字参照『&amp;』に変換しているだけです。そして二度目は文字列「&amp;」に含まれる記号&を文字参照『&amp』に変換しています。結果二度目が終わった後の文字列は「&amp;」となってしまいます。

文字参照中の記号&をさらに文字参照に変換してしまう、これがhtmlspecialcharsを複数回使った場合の問題です。

第四引数double_encodeで問題解消

二重にhtmlspecialchars()を使うと文字参照中の記号&をさらに文字参照に変換してしまう問題が発生するのでした。

もちろん一番簡単な解決方法は二重に使わなければいいだけの話なのですが、システムの都合上どうしてもエスケープ処理を二回してしまう場合もあると思います。htmlspecialchars()関数の第四引数double_encodeにfalseを設定することでエスケープ処理を行いつつ、二重の変換を防ぐことが出来ます。

php


htmlspecialchars(○○, ENT_QUOTES, 'UTF-8', false);

htmlspecialcharsの公式マニュアルでは第四引数double_encodeを以下のように説明されています。

double_encode をオフにすると、PHP は既存の html エンティティをエンコードしません。 デフォルトでは、既存のエンティティも含めてすべてを変換します。

http://php.net/manual/ja/function.htmlspecialchars.php

わかりづらいのですが要は、第四引数double_encodeをfalseに設定するだけで特殊文字(&,”,’,>,<)はきちんと文字参照に変更しつつ、文字参照中の記号&を「ああ、これは文字参照を表す&なんだな。なら変換はやめておこう」と賢く判断してくれるということです。その結果htmlspecialchars()を二重に使ってもおかしな変換をすることなくエスケープすることが可能になります。

double_encodeはfalseがデフォルトでもいいと思う

ということで二重エスケープで表示がおかしくなってしまう場合で、どうしてもhtmlspecialcharsを二重に呼ぶ必要がある場合は第四引数のdouble_encodeをfalseに設定すると治るということでした。

きちんと特殊文字のエスケープ処理も行ってくれるので、デメリットらしいデメリットは少し処理が遅くなるかもしれないことだけです。double_encodeの値はいつもfalseにしていてもいいかもしれませんね