Trusted Types es una función de seguridad que ayuda a prevenir las secuencias de comandos entre sitios basadas en el DOM (XSS del DOM). Para ello, requiere que los datos que se pasan a las APIs del navegador "peligrosas" (conocidas como receptores) se conviertan primero en un tipo específico, como TrustedHTML.
De forma predeterminada, muchas APIs de DOM (como innerHTML o document.write) aceptan cadenas sin procesar. Si una cadena contiene una secuencia de comandos maliciosa de una fuente no confiable, se puede ejecutar. Los Trusted Types cambian este comportamiento: una vez habilitados a través de la CSP, estos receptores solo aceptarán objetos TrustedHTML creados por políticas autorizadas.
Detalles clave de la implementación
Para usar los tipos de confianza, sigue estos cuatro pasos principales:
- Define una política de seguridad que especifique cómo limpiar o transformar las cadenas.
- Usa esa política para crear objetos de tipo de confianza a partir de cadenas sin procesar.
- Usa los objetos de Trusted Types cuando escribas en receptores del DOM.
- Aplica los tipos de confianza con una política de seguridad del contenido (CSP).
1. Crea una política
Una política es un conjunto de reglas para limpiar el contenido. Para crearla, usa trustedTypes.createPolicy(), que suele delegar en una biblioteca de limpieza, como DOMPurify, o en las reglas de escape de HTML de tu aplicación:
import DOMPurify from 'dompurify';
const myPolicy = trustedTypes.createPolicy('sanitize-html', {
createHTML: (input) =>
// Return a sanitized string; createPolicy() wraps it in a TrustedHTML object.
// Example using DOMPurify:
DOMPurify.sanitize(input, { RETURN_TRUSTED_TYPE: false }),
});
// Example using HTML escaping (for environments without third-party sanitizers):
const escapePolicy = trustedTypes.createPolicy('escape-html', {
createHTML: (input) =>
String(input)
.replaceAll('&', '&')
.replaceAll('<', '<')
.replaceAll('>', '>')
.replaceAll('"', '"')
.replaceAll("'", '''),
});
2. Uso de la política
En lugar de pasar una cadena directamente a un receptor, llama al método de creación de la política.
const untrustedInput = '<p>Hello, <strong>world</strong>!</p><img src="x" onerror="alert(1)">';
// In an enforced environment, this would throw a TypeError:
// element.innerHTML = untrustedInput;
// This returns a TrustedHTML object with unsafe elements and attributes removed:
const cleanHTML = myPolicy.createHTML(untrustedInput);
// Result: "<p>Hello, <strong>world</strong>!</p><img src=\"x\">"
3. Transición de los receptores para usar Trusted Types
Actualiza cada instancia en la que escribas en un receptor para convertirlo primero en un objeto de tipo de confianza. Para ayudar a ubicar dónde se debe actualizar el código, puedes definir una política de default temporal que registre cuándo se escribe un receptor con una cadena en lugar de un TrustedType.
trustedTypes.createPolicy("default", {
createHTML(value) {
console.warn("String passed instead of Trusted Type.");
return DOMPurify.sanitize(value, { RETURN_TRUSTED_TYPE: false });
},
});
Entre los receptores comunes que admiten Trusted Types, se incluyen los siguientes:
- HTML:
element.innerHTML,element.outerHTML,document.write(),element.setHTMLUnsafe(),Document.parseHTMLUnsafe() - Guion: Contenido de texto de
<script>,eval() - ScriptURL:
<script src>,Worker(),SharedWorker()
// Assign the TrustedHTML object.
element.innerHTML = cleanHTML;
Ten en cuenta que los métodos de limpieza de HTML "seguros" (element.setHTML() y Document.parseHTML(), que se describen en Cómo limpiar HTML no confiable) no requieren una política de tipos de confianza, ya que siempre limpian los posibles ataques de XSS de forma nativa.
4. Cómo aplicar Trusted Types con CSP
Los tipos de confianza se deben implementar de forma incremental para evitar que se dañe la aplicación. Si bien la aplicación de medidas por incumplimiento completa proporciona la protección más sólida, conlleva el riesgo de que el sitio se rompa si no se migraron todas las escrituras de receptores.
Requisitos previos
- Cooperación del framework: Si el framework de tu aplicación (o cualquier biblioteca de terceros que escriba en receptores del DOM) no produce valores
TrustedHTMLoTrustedScript, no puedes aplicar Tipos de confianza sin romper ese código. Audita la compatibilidad del framework antes de comenzar el lanzamiento. - Refactorización de receptores: Asegúrate de haber identificado y refactorizado los receptores peligrosos para usar tus políticas de Tipos de confianza.
Estrategia de lanzamiento
Solo informes: Primero, realiza el lanzamiento con el encabezado
Content-Security-Policy-Report-Only. Esto registrará los incumplimientos en tu extremo de informes sin bloquear las operaciones de escritura.Content-Security-Policy-Report-Only: require-trusted-types-for 'script';Aplicación de políticas: Una vez que hayas integrado tus políticas y los informes de incumplimiento se reduzcan a cero, pasa a la aplicación de políticas completa.
Content-Security-Policy: require-trusted-types-for 'script';
También puedes restringir qué políticas se pueden crear (enumera los nombres de tus políticas, como sanitize-html o escape-html, incluye dompurify si tu política delega en DOMPurify.sanitize(), lo que crea internamente una política dompurify, y, además, incluye default si registras una política predeterminada durante la migración):
Content-Security-Policy: require-trusted-types-for 'script'; trusted-types sanitize-html escape-html dompurify;
Con esta CSP implementada, cualquier escritura en un receptor que no use tipos de confianza arrojará un error. Además, si se intenta crear una política de tipos de confianza con un nombre que no aparece en trusted-types, se producirá un error.
También puedes establecer esta CSP como una etiqueta <meta> en el <head> de tu documento, aunque esto es potencialmente menos seguro.
<!-- First child of `<head>`, as earlier tags will not enforce this CSP. -->
<meta
http-equiv="Content-Security-Policy"
content="require-trusted-types-for 'script'; trusted-types sanitize-html escape-html dompurify;"
/>
Estrategias de resguardo
En el caso de los navegadores que aún no admiten los tipos de confianza, puedes usar un "tinyfill" mínimo para asegurarte de que el código no se rompa y, al mismo tiempo, proporcionar limpieza.
if (!window.trustedTypes) {
window.trustedTypes = {
createPolicy: (name, rules) => rules,
};
}
Esto te permite escribir tu código con el patrón de tipos de confianza hoy mismo. Los navegadores compatibles aplicarán los tipos, mientras que los navegadores más antiguos seguirán ejecutando tu lógica de limpieza a través de la política simulada.
Si un navegador no admite los tipos de confianza, simplemente tratará los objetos TrustedHTML como cadenas (si se usa el tinyfill) o no tendrá la verificación de aplicación de política a nivel del receptor.