Fecha de publicación: 12 de junio de 2025
El 20 de mayo de 2025, se actualizó la especificación de HTML para escapar de <
y >
en los atributos, lo que ayuda a evitar vulnerabilidades de XSS de mutación (mXSS). Este cambio se implementó en Chrome 138, que se promocionó a la versión beta el 28 de mayo de 2025 y se convertirá en estable el 24 de junio de 2025.
En esta entrada, se detalla el impacto del cambio de escape de atributos HTML en los desarrolladores web y los posibles errores. En nuestra entrada relacionada del blog de Ingeniería de seguridad, se explica la justificación de seguridad detrás de este cambio.
Qué cambió
Supongamos que tienes un elemento <div>
cuyo atributo data-content
tiene un valor de "<u>hello</u>"
. ¿Qué sucede cuando lees div.outerHTML
?
Históricamente, obtendrías el siguiente código HTML:
<div data-content="<u>hello</u>"></div>
Después del cambio, obtendrás el siguiente código HTML:
<div data-content="<u>hello</u>"></div>
Anteriormente, no se escapaban <
ni >
en los atributos. Ahora, ambos caracteres siempre se escapan.
Lo que no cambió
El cambio solo modifica la forma en que los fragmentos HTML se vuelven a convertir en una representación de cadena durante la serialización. El impacto se limita a situaciones en las que se accede a las propiedades innerHTML
o outerHTML
, o cuando se invoca el método getHTML()
en un elemento. Estas operaciones toman la estructura de DOM existente y producen una representación HTML textual.
Este cambio no afecta el análisis de HTML. Considera el siguiente código HTML:
<div id="div1" data-content="<u>hello</u>"></div>
<div id="div2" data-content="<u>hello</u>"></div>
Ambos div
se analizarán de la misma manera y, en ambos casos, div.dataset.content
mostrará "<u>hello</u>"
.
¿Qué no se romperá?
Si usas cualquier API de DOM, como getAttribute
, getAttributeNS
, dataset
o attributes
para recuperar valores de atributos, se mostrarán los mismos valores decodificados que antes, específicamente con <
y >
decodificados.
Considera el siguiente ejemplo, en el que todas las líneas console.log
registrarán "<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);
¿Qué puede romperse?
innerHTML y outerHTML para obtener atributos
Si usas innerHTML
o outerHTML
para extraer el valor de un atributo, el código puede fallar. Considera el siguiente ejemplo, aunque un poco complicado:
<div data-content="<u>"></div>
const div = document.querySelector("div");
const content = div.outerHTML.match(/"([^"]+)"/)[1];
console.log(content);
Este código tendrá un comportamiento diferente después de este cambio. Anteriormente, content
sería igual a "<u>"
, pero ahora es "<u>"
.
Ten en cuenta que no se recomienda analizar HTML con expresiones regulares. Si necesitas obtener un valor de un atributo, usa las APIs de DOM que se describieron en secciones anteriores.
Pruebas de extremo a extremo
Si tienes una canalización de CI/CD en la que empleas Chromium para generar HTML y compilaste pruebas para comparar el HTML con un valor esperado estático, estas pruebas pueden fallar si algún atributo contiene <
o >
.
Esta es una interrupción esperada. Debes actualizar el valor esperado para que todos los caracteres <
y >
se escapen a <
y >,
, respectivamente.
Resumen
En esta entrada de blog, se describió un cambio en la especificación de HTML que hará que los navegadores comiencen a escapar <
y >
en los atributos para mejorar la seguridad, ya que evitará algunas instancias de XSS de mutación.
El cambio estará disponible para todos los usuarios el 24 de junio de 2025 en Chromium (versión 138) y Firefox (versión 140). También se incluye en la versión beta de Safari 26, que debería lanzarse alrededor de septiembre de 2025.
Si crees que este cambio dañó tu sitio web y no tienes una manera fácil de solucionarlo, informa un error en https://issues.chromium.org/.