La secuencia de comandos entre sitios basada en DOM (DOM XSS) ocurre cuando los datos de una fuente controlada por el usuario (como un nombre de usuario o una URL de redireccionamiento tomada del fragmento de la URL) llegan a un receptor, que es una función como eval()
o un método set de propiedades como .innerHTML
que puede ejecutar código JavaScript arbitrario.
DOM XSS es una de las vulnerabilidades de seguridad web más comunes y es común que los equipos de desarrollo lo introduzcan accidentalmente en sus apps. Trusted Types te proporciona las herramientas para escribir, revisar la seguridad y mantener las aplicaciones libres de vulnerabilidades de DOM XSS al hacer que las funciones peligrosas de la API web sean seguras de forma predeterminada. Trusted Types está disponible como polyfill para los navegadores que aún no los admiten.
Información general
Durante muchos años, DOM XSS ha sido una de las vulnerabilidades de seguridad web más frecuentes y peligrosas.
Existen dos tipos de secuencias de comandos entre sitios. Algunas vulnerabilidades XSS se producen por el código del servidor que crea de forma insegura el código HTML que forma el sitio web. Otras tienen una causa raíz en el cliente, en la que el código JavaScript llama a funciones peligrosas con contenido controlado por el usuario.
Para evitar XSS del servidor, no generes HTML mediante la concatenación de cadenas. En su lugar, usa bibliotecas seguras de plantillas de escape automático contextual, junto con una Política de Seguridad del Contenido basada en nonce para una mitigación adicional de errores.
Ahora, los navegadores también pueden ayudar a evitar el XSS basado en DOM del cliente mediante Trusted Types.
Introducción a la API
Trusted Types bloquea las siguientes funciones peligrosas de receptores. Es posible que ya reconozcas algunos de ellos porque los proveedores de navegadores y los frameworks web ya evitan que uses estas funciones por motivos de seguridad.
- Manipulación de secuencias de comandos:
<script src>
y configuración de contenido de texto de los elementos<script>
. - Generar HTML a partir de una cadena:
- Ejecuta el contenido del complemento:
- Compilación de código JavaScript en tiempo de ejecución:
eval
setTimeout
setInterval
new Function()
Trusted Types requiere que proceses los datos antes de pasarlos a estas funciones de receptor. Usar solo una cadena falla porque el navegador no sabe si los datos son confiables:
anElement.innerHTML = location.href;
Para indicar que los datos se procesaron de forma segura, crea un objeto especial, un tipo de confianza.
anElement.innerHTML = aTrustedHTML;
Trusted Types reduce en gran medida la superficie de ataque DOM XSS de tu aplicación. Simplifica las revisiones de seguridad y te permite aplicar de manera forzosa los controles de seguridad basados en tipos que se realizan cuando se compila, analiza con lint o empaquetas el código en el entorno de ejecución en el navegador.
Cómo usar Trusted Types
Prepárate para los informes de incumplimiento de la Política de Seguridad del Contenido
Puedes implementar un recopilador de informes, como go-csp-collector de código abierto o usar uno de los equivalentes comerciales. También puedes depurar las infracciones en el navegador:
document.addEventListener('securitypolicyviolation',
console.error.bind(console));
Agrega un encabezado de la CSP de solo informes
Agrega el siguiente encabezado de respuesta HTTP a los documentos que desees migrar a Trusted Types:
Content-Security-Policy-Report-Only: require-trusted-types-for 'script'; report-uri //my-csp-endpoint.example
Ahora, todos los incumplimientos se informan a //my-csp-endpoint.example
, pero el sitio web sigue funcionando. En la siguiente sección, se explica cómo funciona //my-csp-endpoint.example
.
Identifica infracciones de Trusted Types
A partir de ahora, cada vez que Trusted Types detecte un incumplimiento, el navegador enviará un
informe a un report-uri
configurado. Por ejemplo, cuando tu aplicación pasa una string a innerHTML
, el navegador envía el siguiente informe:
{
"csp-report": {
"document-uri": "https://my.url.example",
"violated-directive": "require-trusted-types-for",
"disposition": "report",
"blocked-uri": "trusted-types-sink",
"line-number": 39,
"column-number": 12,
"source-file": "https://my.url.example/script.js",
"status-code": 0,
"script-sample": "Element innerHTML <img src=x"
}
}
Esto dice que en https://my.url.example/script.js
, en la línea 39, se llamó a innerHTML
con la cadena que comienza con <img src=x
. Esta información debería ayudarte a limitar las partes del código que podrían estar introduciendo DOM XSS y que debas cambiar.
Corrija los incumplimientos
Existen dos opciones para corregir un incumplimiento del tipo de confianza. Puedes quitar el código incorrecto, usar una biblioteca, crear una política de tipo de confianza o, como último recurso, crear una política predeterminada.
Reescribe el código ofensivo
Es posible que el código que no cumple ya no sea necesario o que se pueda reescribir sin las funciones que causan los incumplimientos:
el.textContent = ''; const img = document.createElement('img'); img.src = 'xyz.jpg'; el.appendChild(img);
el.innerHTML = '';
Cómo usar una biblioteca
Algunas bibliotecas ya generan Trusted Types que puedes pasar a las funciones del receptor. Por ejemplo, puedes usar DOMPurify para limpiar un fragmento HTML y quitar las cargas útiles de XSS.
import DOMPurify from 'dompurify';
el.innerHTML = DOMPurify.sanitize(html, {RETURN_TRUSTED_TYPE: true});
DOMPurify admite Trusted Types y muestra HTML depurado unido en un objeto TrustedHTML
para que el navegador no genere un incumplimiento.
Crea una política de tipo de confianza
A veces, no puedes quitar el código que causa el incumplimiento y no hay una biblioteca que desinfecte el valor y cree un tipo de confianza por ti. En esos casos, puedes crear un objeto de tipo de confianza por tu cuenta.
Primero, crea una política. Las políticas son fábricas para Trusted Types que aplican ciertas reglas de seguridad a sus entradas:
if (window.trustedTypes && trustedTypes.createPolicy) { // Feature testing
const escapeHTMLPolicy = trustedTypes.createPolicy('myEscapePolicy', {
createHTML: string => string.replace(/\</g, '<')
});
}
Este código crea una política llamada myEscapePolicy
que puede producir objetos TrustedHTML
mediante su función createHTML()
. Las reglas definidas deben usar caracteres <
de escape HTML para evitar la creación de elementos HTML nuevos.
Usa la política de la siguiente manera:
const escaped = escapeHTMLPolicy.createHTML('<img src=x
console.log(escaped instanceof TrustedHTML); // true
el.innerHTML = escaped; // '<img src=x
Usar una política predeterminada
A veces, no puedes cambiar el código ofensivo, por ejemplo, si cargas una biblioteca de terceros desde una CDN. En ese caso, usa una política predeterminada:
if (window.trustedTypes && trustedTypes.createPolicy) { // Feature testing
trustedTypes.createPolicy('default', {
createHTML: (string, sink) => DOMPurify.sanitize(string, {RETURN_TRUSTED_TYPE: true})
});
}
La política llamada default
se usa cada vez que se usa una string en un receptor que solo
acepta el tipo de confianza.
Cambiar para aplicar la Política de Seguridad del Contenido
Cuando tu aplicación ya no produce incumplimientos, puedes comenzar a aplicar Trusted Types:
Content-Security-Policy: require-trusted-types-for 'script'; report-uri //my-csp-endpoint.example
Sin importar cuán compleja sea tu aplicación web, lo único que puede introducir una vulnerabilidad de DOM XSS es el código en una de tus políticas, y puedes bloquearlo aún más si limitas la creación de políticas.