Publicado em 12 de junho de 2025
Em 20 de maio de 2025, a especificação HTML foi
atualizada para escapar de <
e >
em
atributos, ajudando a evitar vulnerabilidades de XSS de mutação (mXSS, na sigla em inglês). Essa mudança foi lançada no Chrome 138, que foi promovido para Beta em
28 de maio de 2025 e vai se tornar estável em 24 de junho de 2025.
Esta postagem detalha o impacto da mudança de escape de atributos HTML nos desenvolvedores da Web e possíveis falhas. O raciocínio de segurança por trás dessa mudança é explicado na nossa postagem relacionada no blog de engenharia de segurança.
O que mudou
Suponha que você tenha um elemento <div>
cujo atributo data-content
tenha um
valor de "<u>hello</u>"
. O que acontece quando você lê div.outerHTML
?
Historicamente, você receberia o seguinte HTML:
<div data-content="<u>hello</u>"></div>
Depois da mudança, você vai receber o seguinte HTML:
<div data-content="<u>hello</u>"></div>
Antes, nem <
nem >
eram escapados em atributos. Agora, esses dois
caracteres são sempre escapados.
O que não mudou
A mudança modifica exclusivamente como os fragmentos HTML são convertidos de volta em uma
representação de string durante a serialização. O impacto é limitado a cenários
em que as propriedades innerHTML
ou outerHTML
são acessadas ou quando o
método getHTML()
é invocado em um elemento. Essas operações usam a estrutura
DOM existente e produzem uma representação HTML textual.
Essa mudança não afeta a análise de HTML. Considere o seguinte HTML:
<div id="div1" data-content="<u>hello</u>"></div>
<div id="div2" data-content="<u>hello</u>"></div>
Os dois div
s serão analisados exatamente da mesma maneira e, em ambos os casos,
div.dataset.content
vai retornar "<u>hello</u>"
.
O que não vai quebrar?
Se você usar qualquer API DOM, como
getAttribute
,
getAttributeNS
,
dataset
ou
attributes
,
para recuperar valores de atributos, eles vão retornar os mesmos valores decodificados
anteriormente, especificamente com <
e >
decodificados.
Considere o exemplo a seguir, em que todas as linhas console.log
vão registrar
"<u>"
:
<div data-content="<u>"></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);
O que pode quebrar?
innerHTML e outerHTML para receber atributos
Se você usar innerHTML
ou outerHTML
para extrair o valor de um atributo, seu
código poderá ser corrompido. Considere o exemplo a seguir, embora um pouco complicado:
<div data-content="<u>"></div>
const div = document.querySelector("div");
const content = div.outerHTML.match(/"([^"]+)"/)[1];
console.log(content);
Esse código vai apresentar um comportamento diferente após essa mudança. Antes,
content
era igual a "<u>"
, mas agora é "<u>"
.
Não é recomendado analisar HTML com expressões regulares. Se você precisar extrair um valor de um atributo, use as APIs do DOM descritas nas seções anteriores.
Testes de ponta a ponta
Se você tiver um pipeline de CI/CD em que usa o Chromium para gerar HTML e
tiver escrito testes para comparar o HTML a um valor esperado estático, esses testes
poderão falhar se algum atributo contiver <
ou >
.
Esse é um erro esperado. Você precisa atualizar o valor esperado para que todos
os caracteres <
e >
sejam convertidos em <
e >,
, respectivamente.
Resumo
Esta postagem do blog descreveu uma mudança na especificação do HTML que vai fazer com que
os navegadores comecem a escapar de <
e >
em atributos para melhorar a segurança,
evitando algumas instâncias de XSS de mutação.
A mudança vai estar disponível para todos os usuários em 24 de junho de 2025 no Chromium (versão 138) e no Firefox (versão 140). Ele também está incluído na versão Beta do Safari 26, que deve ser lançada por volta de setembro de 2025.
Se você acredita que essa mudança quebrou seu site e não tem uma maneira fácil de corrigir o problema, registre um bug em https://issues.chromium.org/.