htmlspecialcharsでXSSを防ぐ出力エスケープの原則

htmlspecialcharsは、HTMLの特殊文字をエスケープしてXSSを防ぐ関数です。DBに入れる前ではなく出力する瞬間に、ENT_QUOTESと文字コードを明示して使うのが原則ですね。

エスケープは「出力する瞬間」に行う

XSS 対策の基本は、ユーザー由来の値を HTML に埋め込む瞬間にエスケープする、という一点に尽きると思います。DB に入れる前にエスケープしてしまうと、二重エスケープや別用途での再利用で崩れるので、保存はそのまま、出力時に htmlspecialchars、というのが素直なやり方ですね。

$comment = $_POST['comment'] ?? '';
// 出力する瞬間にエスケープ
echo '<p>' . htmlspecialchars($comment) . '</p>';

ENT_QUOTESでシングルクォートも潰す

ここが見落としやすいのですが、htmlspecialchars はデフォルト(PHP8.1 未満)ではシングルクォートを変換しません。属性値をシングルクォートで囲んでいると、値の中の ‘ で属性を抜け出されてしまうので、必ず ENT_QUOTES を指定する癖をつけたいところです。

// 危険:シングルクォート属性を抜けられる可能性
echo "<input value='" . htmlspecialchars($v) . "'>";
// 安全:シングル・ダブル両方をエスケープ
echo "<input value='" . htmlspecialchars($v, ENT_QUOTES) . "'>";

PHP8.1 からはデフォルトが ENT_QUOTES 相当になりましたが、実行環境のバージョンに依存させたくないので、私は明示するようにしています。

第3引数の文字コードも指定する

第三引数のエンコーディングを省略すると、環境によっては意図しない文字コードで解釈され、不正なバイト列を突かれる余地が残ります。UTF-8 のサイトなら ‘UTF-8’ を明示しておくのが安全ですね。毎回書くのは面倒なのでラッパーを一つ用意しておくと楽です。

function e(string $s): string {
    return htmlspecialchars($s, ENT_QUOTES, 'UTF-8');
}
echo '<td>' . e($user->name) . '</td>';

まとめ

XSS 対策は難しい理屈より、出力時にエスケープする・ENT_QUOTES と文字コードを明示する、という運用を徹底するほうが効くと思います。テンプレートエンジンを使っているなら自動エスケープに任せつつ、生 echo する箇所だけはラッパー関数で統一しておくと抜け漏れが減りますね。

よくある質問

Q. エスケープはいつ行えばいいですか。
A. DBに入れる前ではなく、HTMLに出力する瞬間に行います。保存はそのまま、出力時にhtmlspecialcharsが素直ですね。

Q. ENT_QUOTESはなぜ必要なのですか。
A. これがないとシングルクォートが変換されず、シングルクォートで囲んだ属性値を抜け出される余地が残ります。文字コードと合わせて明示するのが安全です。

類似投稿

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です