WordPressテーマの出力エスケープ(esc_* の使い分け)

  • WordPress
  • PHP
  • セキュリティ

テーマやプラグインの PHP から HTML に値を埋め込むとき、そのまま echo すると XSS(クロスサイトスクリプティング)の穴になり得ます。WordPress には用途別のエスケープ関数が用意されているので、出力コンテキストに合わせて関数を選ぶのが基本です。

方針は非公開の個人用メモの WordPress 節と揃えつつ、ここでは公開向けにまとめ直したものです。

ルールの要約(用途と関数)

用途関数
通常テキスト(要素の中身)esc_html()
属性値(title=""data-*、クラス名以外の文字列属性など)esc_attr()
hrefsrc など URL として解釈される場所esc_url()
投稿本文のように タグを限定して HTML を許可したいときwp_kses_post()
許可するタグを独自に決めたい(見出し用に brstrong だけ、など)wp_kses( $html, $allowed_html )

覚え方: 「どこに出すか」で決める。テキストノードなら esc_html、属性の値なら esc_attr、リンク先・画像URLなら esc_url です。

許可タグを細かく決める: wp_kses()

wp_kses_post() は、ざっくり「投稿本文で使えるタグ」と同程度のセットにフィルタする糖衣構文です。もっと狭い集合だけ通したいときは、第2引数に 許可タグの配列 を渡す wp_kses() を使います。

echo wp_kses($heading, $heading_allowed_html);

$heading_allowed_html は WordPress が期待する形式(タグ名をキーに、属性の許可リストを値にする連想配列)です。既存の定義を流用するなら、wp_kses_allowed_html() でベースを取ってから必要なタグだけ残したり、逆に削ったりする書き方もよく使われます。

注意: サニタイズ済みの「HTML 断片」を出力しているので、ここでは esc_html() を重ねない(二重エスケープで意図したマークアップが表示用の文字として壊れる)。属性にユーザー由来の URL などが入る許可セットにしている場合は、別途 wp_kses() のフィルタ内やデータ取得側で URL の妥当性を検討する、というレイヤの話になります。公式は wp_kses() を参照。

なぜ「ユーザー入力じゃないから」と省略しないか

定数や $args 経由のラベルでも、将来のリファクタや別コンポーネントからの流用で 想定外の文字列が渡ることがあります。テーマ内では 出す直前で必ずエスケープする 習慣にしておくと、レビューでも「ここ抜けてないか」の判断が一貫します。

コード例(定数をテンプレートで出す)

<a href="<?php echo esc_url(SNS_INSTAGRAM_URL); ?>">Instagram</a>
<p><?php echo esc_html(COMPANY_TEL); ?></p>

(定数の置き場所などはナレッジ側の php.md の「定数管理」を参照。)

HTML 断片として iframe を定数で持つ場合

マップ埋め込みなど、タグ付きの HTML 断片を定数(例: COMPANY_MAP に iframe のマークアップを入れる)でテンプレートから出すときは、esc_html() だとタグが実体参照になってしまうので、許可するタグ・属性を限定した wp_kses() を使うのが妥当です。

$access_cta_map_allowed_html = [
  'iframe' => [
    'src' => true,
    'title' => true,
    'width' => true,
    'height' => true,
    'style' => true,
    'class' => true,
    'allow' => true,
    'allowfullscreen' => true,
    'loading' => true,
    'referrerpolicy' => true,
    'frameborder' => true,
  ],
];

echo wp_kses( COMPANY_MAP, $access_cta_map_allowed_html );

第2引数の形(タグ名をキーに、属性名を true で列挙する連想配列)は wp_kses() が想定する形式そのものです。title はスクリーンリーダー向けの説明として付けておくのが無難で、コード例にも入れています。allow は埋め込み側がフルスクリーン等の権限を指定するときに使われ、Google マップなどの新しめのコードに含まれることがあります。frameborder は HTML4 世代の埋め込みコピペに残りがちです。class はレイアウト用に付与されることがあります。sandboxname を埋め込みコードが使っているなら、同様に配列へ足してください。

src の URL は許可プロトコルで検査されるため、原則 https: で統一すると安全で、data: などは落ちることがあります。埋め込み側が要求する属性が増えたときはリストに足さないと黙って削られるので、表示が欠ける場合はブラウザの要素検査で属性の有無を確認すると早いです。投稿本文向けの wp_kses_post() だけでは iframe が通りにくいことも多いので、用途ごとに許可配列を定義するのはよくある書き方です。

レガシー API のメモ

他者テーマの調査で attribute_escape() のような古い API が残っていることがあります。新規コードでは esc_attr() を使うのがよい、という整理で足ります(詳細は Developer Resources の該当関数 を参照)。

JavaScript に文字列を渡すとき

テンプレートから <script> 内に PHP の値を埋め込む場合は、HTML エスケープとは別レイヤの話になるため、esc_js() など、文脈に応じた API を選ぶ必要があります。頻度が低い処理ほど見落としやすいので、インライン script で値を渡す設計自体を減らすのも手です。

参照・関連

  • このリポジトリのルール原文: coding-rules/php.md の「WordPress固有」セクション(出力エスケープの表)
  • 公式: Data Validation および各 esc_* のリファレンス

公開 URL のメモ: このサイトの設定では記事のパスは /blog/2026-04-08d です(astro.config.mjssite が本番ドメインになっていれば、https://(本番ドメイン)/blog/2026-04-08d)。