WordPress 7.2 Beta 1でwp_kses()が新しくなった|7.1.3と7.2 Beta 1に同じHTMLを通して比べた出力の違いと戻し方

WordPress 7.2 Beta 1で、wp_kses()がHTML APIを使う実装に変わった

WordPressで投稿やコメントのHTMLから危ない部分を取り除く関数 wp_kses() が、HTML APIを使う実装に作り直されています。WordPressの開発チームは2026年10月7日に新しい実装を開発版へ取り込み、担当したDennis Snell氏が同じ日に変更点を公式ブログで説明しました。10月20日に公開されたWordPress 7.2 Beta 1では、この新しい実装が最初から有効です。Snell氏は、正式版の7.2に入るか7.3に延びるかは、この試験期間に見つかる問題しだいだとしています。7.2の正式版は12月8日の予定です。

wp_kses() は、HTMLを自由に書ける権限(unfiltered_html)を持たないユーザーが保存する投稿やコメント、そしてプラグインやテーマが表示前に通すHTMLを、許可されたタグと属性だけに絞り込みます。これまでは正規表現を組み合わせて文字列を直接いじっていましたが、新しい実装では、WordPress 6.2から入っている「HTML API」でHTMLを構造として読み取り、許可された部分だけを書き出し直します。

呼び出し方は変わらないので、プラグインやテーマのコードを書き換える必要はありません。その一方で、同じ入力から出てくるHTMLは変わります。この記事では、WordPress 7.1.3と7.2 Beta 1を手元のDockerで並べ、同じHTMLを通して出力がどう変わるか、寄稿者が保存した投稿の中身がどう変わるか、旧実装へ一時的に戻す方法を確かめた結果をまとめます。

変更の要点

消えていた文が残り、&とscriptの扱いが変わる

残るようになる

「<5万円」のような文

7.1.3では「<」から「>」までが消えていた。7.2 Beta 1では &lt; として残り、画面には「<」と表示される。

解釈が変わる

本文中の「&section」

ブラウザと同じく「§ion」になる。URLを本文に書くなら &amp; で書く。

消え方が変わる

許可されないscript

タグだけでなく、中のJavaScriptも一緒に消える。

試験中の切り替え

フィルターで新旧を比べられる

wp_kses_force_legacy_parser をtrueにすると、7.1.3と同じ出力になった。

2026年10月27日、Docker上のWordPress 7.1.3と7.2 Beta 1(PHP 8.3.36)で wp_kses_post() に同じ入力を通して確認

新しい実装は、7.2 Beta 1を入れると最初から有効になっている

7.2 Beta 1の wp-includes/kses.php を見ると、wp_kses() は最初にフィルター wp_kses_force_legacy_parser の値を確かめ、falseなら新しい実装 wp_sanitize_html_kses() へ処理を渡します。そして wp-includes/default-filters.php で、このフィルターにfalseを返す関数が登録されています。つまり7.2 Beta 1に上げると、何も設定しなくても新しい実装が動きます。

新しい実装を取り込んだコミットは、旧実装も当面は残し、フィルターで切り替えられるようにしています。ただしSnell氏は、旧実装は将来のリリースで、テストの結果によっては7.2.0の時点でなくなる可能性があると書いています。

2026年8月25日

新しい実装のプルリクエスト(PR #13271)が作られる。

2026年10月7日

開発版へ取り込まれ、Snell氏が変更点の説明を公開。10月9日には、ブロックの区切りに使うコメント(<!-- wp:paragraph --> など)を整える修正が続いた。

2026年10月20日

WordPress 7.2 Beta 1公開。新しい実装が既定で有効。

2026年12月8日(予定)

WordPress 7.2の正式版。新しい実装が7.2に入るか7.3に延びるかは、試験の結果で決まる。

同じHTMLを通すと、10件中6件で7.1.3と出力が変わった

手元のDockerに公式イメージのWordPress 7.1.3を2つ立て、片方をWP-CLIの wp core update --version=7.2-beta1 --force で7.2 Beta 1に上げました。投稿の本文に使われる許可範囲で絞り込む wp_kses_post() に10件の入力を通したところ、6件で出力が変わり、4件は同じでした。10件のうち5件には、Snell氏の説明に載っている例をそのまま使っています。主なものを並べます。

1. 「<」と「>」を含む文

入力(筆者が作った日本語の例)

予算は<5万円、重さは>2kgの候補から選びます。

7.1.3

予算は2kgの候補から選びます。

7.2 Beta 1

予算は&lt;5万円、重さは&gt;2kgの候補から選びます。

2. 本文に書いたURLの「&」

入力(Snell氏の説明の例)

Add <code>/?preview=true&section=grilling</code>.

7.1.3

…/?preview=true&amp;section=grilling…

7.2 Beta 1

…/?preview=true§ion=grilling…

3. 許可されないscript

入力(Snell氏の説明の例)

Click on my <script>alert(1)</script>!

7.1.3

Click on my alert(1)!

7.2 Beta 1

Click on my !

4. 大文字のタグと重複した属性

入力(Snell氏の説明の例)

The <IMG class=bike class="bo&#x0061;t" src='vehicle.png' /> & the wheels&hellip;

7.1.3

The <IMG class="bike" src="vehicle.png" /> &amp; the wheels&hellip;

7.2 Beta 1

The <img class="bike" src="vehicle.png"> &amp; the wheels…

4のように、新しい実装はHTMLを読み取ってから書き出し直すため、タグ名は小文字になり、<img> の末尾の / は付かなくなり、&hellip; のような記号は文字(…)に置き換わります。属性を二重引用符で囲む点と、同じ属性が2つあるときに最初のものだけを残す点は、7.1.3と同じでした。

残りの2件は、Snell氏の説明にある英語の例(「<$500」と「>5yo」を含む文。1と同じ結果)と、途中で切れたタグです。Click on the <butto は、7.1.3では &lt;butto と文字にして残り、7.2 Beta 1では切れた部分ごと削られて Click on the になりました。

一方、HTMLコメント(<!-- note -->)、投稿の許可範囲に入っていないSVG、閉じタグのない <div class="wrap"><p>first part の3件と、もともと &amp; と書いたURLは、両方の版で同じ出力でした。閉じタグを補わないのは意図して残した挙動だと、Snell氏も説明しています。別々に絞り込んだHTMLの断片をつなげて使うコードが多いためです。

「<5万円」が消えなくなり、本文のURLは&amp;で書く必要がある

変化の多くは、HTML APIがブラウザと同じ規則でHTMLを読むために起きます。旧実装は「<」を見るとタグの始まりとして扱い、次の「>」までをまとめて消していました。このため日本語の文で「<5万円」「>2kg」のように不等号を使うと、その間の文が丸ごと消えていました。新しい実装は、ブラウザと同じく「<5」をタグとは読まないため、文字として残します。

反対に、気をつけたいのが「&」です。HTMLには、末尾のセミコロンがなくても記号として読まれる名前(&sect や &copy など)があります。ブラウザは本文中の &section を「§ion」と表示します。旧実装はこれを &amp;section と書き換えて文字どおり表示させていましたが、新しい実装はブラウザと同じく「§」に変えます。Snell氏は、HTMLに「&」を書くときは常に &amp; とし、記号の名前には必ずセミコロンを付けるよう勧めています。

この違いが表に出るのは、wp_kses() を通るHTMLです。投稿者や寄稿者がこれから保存する(または保存し直す)本文、コメント、プラグインが wp_kses() 系の関数に通して表示・保存するHTMLに、?a=1&section=2 のような「&」が入る場合は、&amp; で書いておくと7.1.3と同じ表示になります。

寄稿者が保存した投稿は、保存される中身そのものが変わる

wp_kses() は表示のときだけでなく、保存のときにも動きます。WordPressは、unfiltered_html を持たないユーザーが投稿や抜粋、コメントを保存するとき、本文を絞り込んでからデータベースへ入れます。単一サイトでは管理者と編集者がこの権限を持ち、投稿者と寄稿者は持っていません。

WP-CLIで寄稿者のユーザーを作り、同じ本文の投稿を両方の版で保存して、データベースに入った中身を読み出しました。

$ wp post create --post_title=test --user=contrib --porcelain \
    --post_content='予算は<5万円、重さは>2kgの候補から選びます。Add <code>/?preview=true&section=grilling</code>. <script>alert(1)</script>'

# 7.1.3 で保存された post_content
予算は2kgの候補から選びます。Add <code>/?preview=true&amp;section=grilling</code>. alert(1)

# 7.2 Beta 1 で保存された post_content
予算は&lt;5万円、重さは&gt;2kgの候補から選びます。Add <code>/?preview=true§ion=grilling</code>. 

ブロックの投稿でも試しました。段落ブロックの &copy 2026 は、7.1.3では &amp;copy 2026、7.2 Beta 1では © 2026 として保存されました。画像の <IMG SRC="a.png" alt=x /> は、7.1.3では <IMG src="a.png" alt="x" />、7.2 Beta 1では <img alt="x" src="a.png"> として保存されています(属性の並び順も変わりました)。<!-- wp:paragraph {"fontSize":"large"} --> のようなブロックの区切りコメントと属性は、両方の版でそのまま残りました。

同じ本文を管理者で保存すると、両方の版で入力のまま保存されました。コメントで使う許可範囲(wp_kses_data())にも、予算は<5万円、重さは>2kgです。<b>注意</b><script>x()</script> を通して比べました。7.1.3では 予算は2kgです。<b>注意</b>x()、7.2 Beta 1では 予算は&lt;5万円、重さは&gt;2kgです。<b>注意</b> になり、投稿と同じ傾向でした。

wp_kses()の変更で出力が変わる場面

変わる

投稿者・寄稿者がこれから保存する投稿と抜粋、unfiltered_html を持たない人のコメント

変わる

プラグインやテーマが wp_kses()、wp_kses_post()、wp_kses_data() を通して表示・保存するHTML(表示のたびに通すものは、保存済みの内容でも表示が変わる)

変わらない

単一サイトの管理者・編集者が保存する投稿(unfiltered_html を持つため絞り込まれない)

変わらない

データベースに保存済みの投稿の中身(投稿者や寄稿者が保存し直すと、そのときに新しい規則で絞り込まれる)

旧実装へ戻すフィルターは、原因を調べるあいだだけ使う

試験中の比較や、7.2 Beta 1で表示が崩れたときの一時的な回避のために、旧実装へ戻すフィルターが用意されています。Snell氏の説明にある書き方は次の1行です。

add_filter( 'wp_kses_force_legacy_parser', '__return_true' );

7.2 Beta 1のサイトで、この1行だけを書いたPHPファイルを wp-content/mu-plugins フォルダに置くと、「予算は<5万円、重さは>2kgの候補から選びます。」の出力は、7.1.3と同じ「予算は2kgの候補から選びます。」に戻りました。比較用のスクリプトでも同じフィルターを使って10件を通し直し、出力はすべて7.1.3と一致しました。

このフィルターは、どこから来たHTMLかを区別せず、サイト全体の wp_kses() を旧実装に戻します。コメントのような外部からの入力も旧実装で絞り込まれ、新しい実装で直った不具合(コミットでは4件のチケットを修正済みとしています)も元に戻ります。また、旧実装がなくなった版では、このフィルターは効かなくなる見込みです。本番で使う場合は、試験環境で崩れる入力を特定し、入力の側(&の書き方など)を直すまでの短い期間に限ります。崩れた入力はチケット #66208 へ報告するよう、Snell氏は呼びかけています。

手元で試すなら、7.1.3と7.2 Beta 1に同じ入力を通して比べる

自分のプラグインやサイトで出力が変わるかは、同じ入力を両方の版の wp_kses_post() に通して比べると分かります。本番のサイトではなく、Dockerや7.2 Beta 1の案内にあるWordPress Playgroundなどの試験環境で行います。筆者が使ったのは次のような最小のスクリプトです。

<?php
// cases.php: wp eval-file cases.php [legacy]
if ( isset( $args[0] ) && 'legacy' === $args[0] ) {
	add_filter( 'wp_kses_force_legacy_parser', '__return_true' );
}
$cases = array(
	'ja_lt'     => '予算は<5万円、重さは>2kgの候補から選びます。',
	'ampersand' => 'Add <code>/?preview=true&section=grilling</code>.',
	// 自分のプラグインが受け取るHTMLを足す
);
foreach ( $cases as $k => $html ) {
	echo $k, ': ', wp_kses_post( $html ), "\n";
}

7.2 Beta 1のサイトで wp eval-file cases.php と wp eval-file cases.php legacy を実行すると、新旧の出力を同じサイトで並べられます。比べる入力には、自分のサイトに実際に届いたコメントや、プラグインが外部から受け取って表示するHTMLを選ぶと、影響を早く見つけられます。

7.2 Beta 1で確認する3項目

1. wp_ksesを通る&

投稿者・寄稿者の本文、コメント、プラグインが通すHTMLにある「&」を &amp; で書く。

2. プラグインの出力

wp_kses系の関数に通しているHTMLを、7.1.3と7.2 Beta 1で比べる。

3. 崩れた入力の報告

崩れる入力を見つけたら #66208 へ報告する。旧実装へ戻すのは原因を直すまでの短い期間だけにする。

投稿者や寄稿者が書く記事やコメントを受け付けているサイトでは、新しい実装が入った版に上げると、不等号を含む文がそのまま残るようになります。12月の7.2正式版までに、試験環境で自分のサイトの入力を一度通しておくと、どの表示が変わるかを前もって把握できます。