Reduce la superficie de ataque DOM XSS de tu aplicación.
¿Por qué debería preocuparte?
El cross-site scripting (secuencias de comandos en sitios cruzados) basado en DOM (DOM XSS) es una de las vulnerabilidades de la seguridad web más comunes y fáciles de introducir en tu aplicación. Trusted Types te brinda las herramientas para escribir, revisar la seguridad y mantener las aplicaciones libres de vulnerabilidades DOM XSS haciendo que las peligrosas funciones de la API web sean seguras de manera predeterminada. Trusted Types es compatible con Chrome 83 y cuenta con un polyfill disponible para otros navegadores. Consulta Compatibilidad con navegadores para obtener información actualizada sobre compatibilidad con varios navegadores.
Antecedentes
Durante muchos años, DOM XSS ha sido una de las vulnerabilidades de seguridad web más frecuentes y peligrosas.
Hay dos grupos distintos de secuencias de comandos entre sitios. Algunas vulnerabilidades XSS son causadas por el código del lado del servidor que crea de manera insegura el código HTML que forma el sitio web. Otras tienen su origen en el cliente, donde el código JavaScript llama a funciones peligrosas con contenido controlado por el usuario.
Para evitar XSS del lado del servidor, no generes HTML concatenando cadenas y utiliza bibliotecas de plantillas seguras con escape automático contextual. Usa una Política de seguridad de contenido basada en nonce para una mitigación adicional contra los errores, ya que inevitablemente se producen.
Ahora, un navegador también puede ayudar a prevenir los XSS del lado del cliente (también conocidos como basados en DOM) con Trusted Types.
Introducción a la API
Trusted Types funciona bloqueando las siguientes funciones de sumidero de riesgo. Es posible que ya reconozcas algunas de ellas, ya que los proveedores de navegadores y los marcos web ya te alejan del uso de estas funciones por razones de seguridad.
Manipulación de guiones :
<script src>
y configurar el contenido del texto de los elementos<script>
.Generación de HTML a partir de una cadena:
innerHTML
,outerHTML
,insertAdjacentHTML
,<iframe> srcdoc
,document.write
,document.writeln
yDOMParser.parseFromString
Ejecutar contenido del complemento (plugin):
<embed src>
,<object data>
y<object codebase>
Compilar código JavaScript en tiempo de ejecución:
eval
,setTimeout
,setInterval
,new Function()
Trusted Types requiere que se procesen los datos antes de pasarlos a las funciones de sumidero mencionadas. El simple hecho de usar una cadena fallará, ya que 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 Trusted Types.
anElement.innerHTML = aTrustedHTML;
Trusted Types reduce considerablemente la superficie de ataque DOM XSS de tu aplicación. Simplifica las revisiones de seguridad y te permite hacer cumplir las verificaciones de seguridad basadas en tipos que se realizan al compilar, enlazar o empaquetar tu código en tiempo de ejecución, en el navegador.
Cómo utilizar Trusted Types
Prepárate para los informes de infracción de la política de seguridad de contenido
Puedes implementar un recopilador de informes (como el de código abierto go-csp-collector) o utilizar uno de los equivalentes comerciales. También puedes depurar las infracciones en el navegador:
window.addEventListener('securitypolicyviolation',
console.error.bind(console));
Agregar un encabezado de CSP solo para informes
Agrega el siguiente encabezado de respuesta HTTP a los documentos que deseas migrar a Trusted Types.
Content-Security-Policy-Report-Only: require-trusted-types-for 'script'; report-uri //my-csp-endpoint.example
Ahora todas las infracciones se informan a //my-csp-endpoint.example
, pero el sitio web sigue funcionando. La siguiente sección explica cómo funciona //my-csp-endpoint.example
Identificar infracciones de Trusted Types
A partir de ahora, cada vez que Trusted Types detecte una infracción, se enviará un informe a un report-uri
configurado. Por ejemplo, cuando tu aplicación pasa una cadena 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
la línea 39 innerHTML
fue llamado con la cadena que comienza con <img src=x
. Esta información debería ayudarte a delimitar qué partes del código pueden estar introduciendo DOM XSS y deben cambiar.
Resolver las infracciones
Hay un par de opciones para corregir una infracción de Trusted Type. Puedes eliminar el código infractor, utilizar una biblioteca, crear una política de Trusted Type o, como último recurso, crear una política predeterminada.
Vuelve a escribir el código infractor
¿Quizás la funcionalidad no conforme ya no sea necesaria o se pueda reescribir de una manera moderna sin usar las funciones propensas a errores?
el.innerHTML = '';
el.textContent = '';
const img = document.createElement('img');
img.src = 'xyz.jpg';
el.appendChild(img);
Usa una biblioteca
Algunas bibliotecas ya generan tipos de confianza que puede pasar a las funciones de receptor. Por ejemplo, puedes usar DOMPurify para desinfectar un fragmento de HTML, eliminando cargas útiles XSS.
import DOMPurify from 'dompurify';
el.innerHTML = DOMPurify.sanitize(html, {RETURN_TRUSTED_TYPE: true});
DOMPurify admite Trusted Types y devolverá HTML desinfectado envuelto en un TrustedHTML
de manera que el navegador no genere una infracción.
Crea una política de Trusted Type
A veces no es posible eliminar la funcionalidad y no existe una biblioteca para desinfectar el valor y crear un tipo de confianza para ti. En esos casos, crea para ti mismo un objeto Trusted Type.
Para eso, primero crea una política. Las políticas son fábricas de tipos de confianza que imponen ciertas reglas de seguridad en 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 TrustedHTML
través de su función createHTML()
. Las reglas definidas con HTML-escape <
caracteres evitarán la creación de nuevos elementos HTML.
Utiliza la política de esta manera:
const escaped = escapeHTMLPolicy.createHTML('<img src=x onerror=alert(1)>');
console.log(escaped instanceof TrustedHTML); // true
el.innerHTML = escaped; // '<img src=x onerror=alert(1)>'
Utiliza una política predeterminada
A veces no se puede cambiar el código infractor. Por ejemplo, este es el caso si está cargando una biblioteca de terceros desde una CDN. En ese caso, utilice 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 con un nombre default
se usa siempre que se usa una cadena en un receptor que solo acepta Trusted Type.
Pasa a aplicar la política de seguridad de contenidos
Cuando su aplicación ya no produzca infracciones, puede comenzar a hacer cumplir los tipos de confianza:
Content-Security-Policy: require-trusted-types-for 'script'; report-uri //my-csp-endpoint.example
¡Ya está! Ahora, no importa cuán compleja sea tu aplicación web, lo único que puede introducir una vulnerabilidad DOM XSS es el código en una de tus políticas, y puedes bloquearlo aún más limitando la creación de políticas.