

json_encodeでHTML埋め込み用に安全にエスケープする
この記事は、構成と文章の整理にAIを使っています。
あくまで読みやすくするための補助で、内容そのものをAIにすべて任せて書いているわけではありませんが、ところどころ相談しながら整えています。
何のためのオプションか
PHPのjson_encode()をHTMLの中(<script>タグ内とか、属性値とか)に埋め込む用途で使う場合、そのままだと<や"や&がエスケープされずに出力されて、XSSの原因になり得る。これを防ぐための組み合わせが以下。
public function jsonSafeEncode($data) { return json_encode($data, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT);}4つのフラグをORで組み合わせて、HTML的に危険な文字を軒並みUnicodeエスケープに変換する狙い。
各フラグが対象にしている文字
| フラグ | 対象文字 | 変換後 |
|---|---|---|
JSON_HEX_TAG |
< > (U+003C, U+003E) |
\u003C \u003E |
JSON_HEX_QUOT |
" (U+0022) |
\u0022 |
JSON_HEX_AMP |
& (U+0026) |
\u0026 |
JSON_HEX_APOS |
' (U+0027) |
\u0027 |
どれもASCII範囲の記号だけが対象で、見た目が似ているUnicodeの記号(全角引用符とか)は含まれない。
たとえばJSON_HEX_APOSは半角のアポストロフィ(U+0027)だけを変換する。
json_encode("it's", JSON_HEX_APOS);// => "it\u0027s" 半角アポストロフィは変換される
json_encode("it’s", JSON_HEX_APOS);// => "it’s" U+2019(カーブクォート)はそのまま、対象外U+2019のような見た目が似ている引用符はエスケープ対象にならない。これをエスケープするかどうかはJSON_UNESCAPED_UNICODEの有無の話で、JSON_HEX_APOSとは別軸。自分でメモを書いてたときにこの2つの文字を混同してて、エディタの自動置換かコピペで半角'が見た目の似た’に化けてたのが原因だった。
このメモに無かった注意点
- デフォルト(フラグ無し)だと
/が\/にエスケープされる。これを避けたいならJSON_UNESCAPED_SLASHESが別途要る。今回の4フラグには含まれてない。 JSON_HEX_TAG|AMP|APOS|QUOTの組み合わせはHTML埋め込み用のXSS対策として定番で、jsonSafeEncodeのような実装自体は妥当。
分かったこと
- HEX系フラグが対象にしているのはASCII範囲の記号(
<>"&')だけ - 見た目が似ているUnicodeの記号(
’など)はエスケープ対象外 - HTML埋め込み用に安全化したいなら
JSON_HEX_TAG|JSON_HEX_AMP|JSON_HEX_APOS|JSON_HEX_QUOTの組み合わせが定番、/も避けたいならJSON_UNESCAPED_SLASHESも追加する