Zmiana specyfikacji HTML: kodowanie ucieczki znaków < i > w atrybutach

Michał Bentkowski
Michał Bentkowski

Data publikacji: 12 czerwca 2025 r.

20 maja 2025 r. specyfikacja HTML została zaktualizowana, aby ucieczki < i > w atrybutach były traktowane jako ucieczki, co pomaga zapobiegać podatnościom na ataki mutacji XSS (mXSS). Ta zmiana została wprowadzona w Chrome 138, która 28 maja 2025 r. została przeniesiona do wersji beta, a 24 czerwca 2025 r. stanie się stabilną.

W tym poście opisujemy wpływ zmiany dotyczącą ucieczki atrybutów HTML na programistów i potencjalne problemy. Motywy bezpieczeństwa leżące u podstaw tej zmiany zostały opisane w powiązanym poście na blogu poświęconym bezpieczeństwu.

Co się zmieniło?

Załóżmy, że masz element <div>, którego atrybut data-content ma wartość "<u>hello</u>". Co się stanie, gdy przeczytasz div.outerHTML?

Wcześniej otrzymywano taki kod HTML:

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

Po wprowadzeniu zmiany otrzymasz następujący kod HTML:

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

Wcześniej ani <, ani > nie były ujęte w cudzysłowie w atrybutach. Obecnie oba te znaki są zawsze ujęte w znaki ucieczki.

Co się nie zmienia?

Zmiana dotyczy wyłącznie sposobu, w jaki fragmenty kodu HTML są konwertowane z powrotem na ciąg znaków podczas serializacji. Wpływ jest ograniczony do scenariuszy, w których uzyskuje się dostęp do właściwości innerHTML lub outerHTML, lub gdy wywoływana jest metoda getHTML() elementu. Te operacje wykorzystują istniejącą strukturę DOM i tworzą tekstową reprezentację HTML.

Ta zmiana nie wpływa na analizowanie kodu HTML. Weź pod uwagę ten kod HTML:

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

Oba div zostaną przeanalizowane dokładnie w ten sam sposób i w obu przypadkach funkcja div.dataset.content zwróci wartość "<u>hello</u>".

Co się nie zepsuje?

Jeśli do pobierania wartości atrybutów używasz dowolnego interfejsu DOM API, np. getAttribute, getAttributeNS, dataset lub attributes, zwróci on te same odkodowane wartości co wcześniej, w szczególności w przypadku atrybutów <>.

Rozważ ten przykład, w którym wszystkie wiersze console.log będą rejestrowane w "<u>":

<div data-content="&lt;u&gt;"></div>
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);

Co może się zepsuć?

Pobieranie atrybutów za pomocą innerHTML i outerHTML

Jeśli do wyodrębniania wartości atrybutu używasz funkcji innerHTML lub outerHTML, Twój kod może przestać działać. Rozważ ten nieco zawiły przykład:

<div data-content="<u>"></div>
const div = document.querySelector("div");
const content = div.outerHTML.match(/"([^"]+)"/)[1];
console.log(content);

Po tej zmianie kod będzie działał inaczej. Wcześniej content byłoby równe "<u>", ale teraz jest to "&lt;u&gt;".

Pamiętaj, że analizowanie kodu HTML za pomocą wyrażeń regularnych nie jest zalecane. Jeśli chcesz pobrać wartość atrybutu, użyj interfejsów DOM opisanych w poprzednich sekcjach.

Testy kompleksowe

Jeśli masz potok CI/CD, w którym do generowania kodu HTML używasz Chromium, i masz napisane testy porównujące kod HTML ze statyczną wartością oczekiwaną, te testy mogą się nie uruchomić, jeśli jakikolwiek atrybut zawiera znaki < lub >.

Jest to oczekiwane uszkodzenie – musisz zaktualizować oczekiwaną wartość, tak aby wszystkie znaki < i > zostały odpowiednio zastąpione znakami &lt; i &gt;,.

Podsumowanie

W tym poście na blogu opisaliśmy zmianę w specyfikacji HTML, która spowoduje, że przeglądarki zaczną stosować znak ucieczki < i > w atrybutach, aby zwiększyć bezpieczeństwo poprzez zapobieganie niektórym przypadkom mutacji XSS.

Ta zmiana będzie dostępna dla wszystkich użytkowników 24 czerwca 2025 r. w przeglądarce Chromium (w wersji 138) i Firefoksie (w wersji 140). Jest ona też dostępna w wersji beta Safari 26, która powinna zostać wydana we wrześniu 2025 r.

Jeśli uważasz, że ta zmiana spowodowała problemy z Twoją witryną i nie wiesz, jak je łatwo rozwiązać, zgłoś błąd na stronie https://issues.chromium.org/.

Dodatkowe informacje