---
title: HTMLに属性値をエスケープする破壊的変更が導入へ
url: https://roboin.io/article/2025/06/16/html-introduces-breaking-change-in-attribute-value-escaping/
description: 2025年5月20日、HTML仕様が更新され、mutation XSS（mXSS）脆弱性を防ぐために属性内の<および>をエスケープするようになりました。
publishedDate: 2025-06-16T09:43:50.600Z
modifiedDate: 2025-06-16T09:43:50.600Z
thumbnail: https://roboin.io/api/og/2025/06/16/html-introduces-breaking-change-in-attribute-value-escaping/
thumbnailAlt: 記事のサムネイル画像
thumbnailCaption: N/A
---

# HTMLに属性値をエスケープする破壊的変更が導入へ

:::note
この記事は、[Google が作成、共有する](https://developers.google.com/readme/policies?hl=ja)、[Creative Commons Attribution 4.0 License](https://creativecommons.org/licenses/by/4.0/)でライセンスされた“[HTML spec change: escaping < and > in attributes  |  Blog  |  Chrome for Developers](https://developer.chrome.com/blog/escape-attributes?hl=en)”を日本語に翻訳したものです。
:::

2025年5月20日、[HTML仕様が更新され](https://github.com/whatwg/html/pull/6362)、[mutation XSS](https://cure53.de/fp170.pdf)（mXSS）脆弱性を防ぐために属性内の`<`および`>`をエスケープするようになりました。この変更は、2025年5月28日にベータチャンネルに昇格し、2025年6月24日に安定版になるChrome 138に実装されます。

本記事では、HTML属性のエスケープに関する変更がWeb開発者に与える影響や、想定される破壊的変更について説明します。変更のセキュリティ的背景は、[Security Engineeringブログの関連投稿](https://bughunters.google.com/blog/5038742869770240)にて詳しく解説されています。

## 何が変わったのか

たとえば、`data-content` 属性に `"<u>hello</u>"` を持つ `<div>` 要素があるとします。このとき、`div.outerHTML` を読み取るとどうなるでしょうか？

従来は、次のようなHTMLが得られました。

```html
<div data-content="<u>hello</u>"></div>
```

変更後は、次のようになります。

```html
<div data-content="&lt;u&gt;hello&lt;/u&gt;"></div>
```

以前は属性内の `<` や `>` はエスケープされませんでした。今後、これらの文字列は常にエスケープされます。

## 変わらないこと

今回の変更は、HTMLフラグメントが文字列としてシリアライズされるときの挙動にのみ影響します。影響を受けるのは、`innerHTML` や `outerHTML`にアクセスするケースや、要素に対して `getHTML()` メソッドを呼び出すようなケースのみです。これらの操作は、既存のDOM構造を受け取り、HTML文字列を生成します。

この変更はHTMLのパース処理には**影響しません**。次のようなHTMLを考えてみましょう。

```html
<div id="div1" data-content="<u>hello</u>"></div>
<div id="div2" data-content="&lt;u&gt;hello&lt;/u&gt;"></div>
```

どちらの`div`も同じようにパースされ、`div.dataset.content` の結果は両方とも `"<u>hello</u>"` になります。

## 何が壊れないのか？

`getAttribute`、`getAttributeNS`、`dataset`、`attributes` など、DOM APIを用いて属性値を取得する場合、以前と同じデコードされた値、とくに`<` と `>` がデコードされた値が返されます。

以下の例では、すべての `console.log` が `"<u>"` を出力します。

```html
<div data-content="&lt;u&gt;"></div>
```

```javascript
const div = document.querySelector("div");
// All of the following will log "<u>"
console.log(div.getAttribute("data-content"));
console.log(div.dataset.content);
console.log(div.attributes['data-content'].value);

```

## 何が壊れる可能性があるか？

### innerHTMLやouterHTMLで属性値を取得している場合

属性値の抽出に `innerHTML` や `outerHTML` を使用している場合、コードが壊れる可能性があります。たとえば次のような少し込み入った例を考えます。

```html
<div data-content="<u>"></div>
```

```javascript
const div = div.querySelector("div");
const content = div.outerHTML.match(/"([^"]+)"/)[1];
console.log(content);

```

このコードは、変更後は異なる挙動になります。以前は `content` は `"<u>"` でしたが、今後は `"&lt;u&gt;"` になります。

なお、[正規表現でHTMLをパースするのは推奨されない](https://stackoverflow.com/a/1732454)ことに注意してください。属性値を取得するには、前述のDOM APIを利用してください。

### エンドツーエンドテスト

ChromiumでHTMLを生成するCI/CDパイプラインで、期待される静的なHTMLと比較するようなテストを実行している場合、属性値に `<` や `>` を含んでいるとテストが失敗する可能性があります。

これは想定された破壊的変更であり、`<` と `>` をそれぞれ `&lt;` と `&gt;` にエスケープした新しい期待値に更新する必要があります。

## まとめ

本記事では、HTML仕様の変更により、属性内の `<` と `>` がエスケープされるようになり、一部のmutation XSSを防ぐことでセキュリティが向上することを紹介しました。

この変更は、2025年6月24日のChromium（バージョン138）とFirefox（バージョン140）ですべてのユーザーに適用されます。また、2025年9月ごろにリリース予定のSafari 26 Betaにも含まれる予定です。

もしこの変更によってウェブサイトが壊れてしまい、簡単に修正できない場合は、[https://issues.chromium.org/](https://issues.chromium.org/) にバグを報告してください。

## 追加情報

- [この変更の提案に関するもとのバグレポート](https://github.com/whatwg/html/issues/6235)
- [仕様を変更したプルリクエスト](https://github.com/whatwg/html/pull/6362)
- [ChromeStatusのエントリ](https://chromestatus.com/feature/6264983847174144)
- [この変更のセキュリティ的背景に関するGoogle Security Engineeringブログの投稿](https://bughunters.google.com/blog/5038742869770240)

---

“[HTML spec change: escaping < and > in attributes  |  Blog  |  Chrome for Developer](https://developer.chrome.com/blog/escape-attributes?hl=en)” by Google, licensed under [Creative Commons Attribution 4.0 License](https://creativecommons.org/licenses/by/4.0/) / translated into Japanese by ろぼいん