Kodokon kodokon.com

Seguridad del lado de HTML: XSS, sandbox, noopener y CSP

Identifica los vectores de XSS propios del marcado y despliega las defensas nativas de HTML: sandbox, noopener y Content Security Policy.

11 min · 3 preguntas

Abrir esta lección en Kodokon

El contexto de escape marca toda la diferencia: un valor inofensivo en un nodo de texto se vuelve ejecutable en un atributo. El atributo sin comillas es el peor caso: un solo espacio en los datos inyectados basta para crear un nuevo atributo, por ejemplo un manejador onmouseover. Incluso cuando está correctamente entrecomillado, un valor sigue siendo peligroso en sinks sensibles: href y src aceptan el esquema javascript:, formaction secuestra el envío de un formulario, y srcdoc interpreta sus entidades como HTML una vez decodificadas.

HTML
<!-- userName = x onmouseover=alert(1) -->
<img src="avatar.png" alt=x onmouseover=alert(1)>
<!-- The unquoted attribute lets a handler in -->
Datos sin escapar en un atributo sin comillas se convierten en código.

El atributo sandbox de un iframe elimina todos los privilegios, y luego los vuelves a otorgar uno por uno: allow-scripts, allow-forms, allow-popups... Sin allow-same-origin, el documento incrustado recibe un origen opaco: sin cookies, sin almacenamiento, sin acceso al DOM del padre - incluso si se sirve desde tu propio dominio.

HTML
<iframe
  src="https://widget.example.com"
  sandbox="allow-scripts allow-forms"
  referrerpolicy="no-referrer">
</iframe>
Un widget de terceros que puede ejecutar scripts, pero confinado a un origen opaco.

Un enlace target=_blank daba históricamente a la página abierta una referencia window.opener a tu pestaña: podía redirigir tu página a un clon de phishing, el ataque conocido como tabnabbing. Los navegadores modernos ahora aplican noopener de forma implícita en target=_blank, pero deletrearlo sigue siendo esencial para window.open en JavaScript y para documentar la intención; añade noreferrer para suprimir también la cabecera Referer.

HTML
<a href="https://external.example"
   target="_blank"
   rel="noopener noreferrer">
  View the external resource
</a>
La página abierta recibe window.opener === null.

Una Content Security Policy restringe las fuentes de código ejecutable y neutraliza la mayoría de los XSS, incluso cuando la inyección ha tenido éxito. Declarada mediante una etiqueta <meta http-equiv>, tiene sin embargo puntos ciegos: las directivas frame-ancestors, report-uri y sandbox se ignoran ahí y requieren la cabecera HTTP. Prefiere nonces regenerados en cada respuesta antes que listas de dominios permitidos, que la directiva strict-dynamic vuelve obsoletas de todos modos.

HTML
<meta http-equiv="Content-Security-Policy"
      content="default-src 'self';
               script-src 'self' 'nonce-r4nd0m'">
Solo se ejecutan los scripts que llevan el nonce del servidor.

Prueba de conocimientos

Comprueba que has retenido los puntos clave de esta lección.

  1. ¿Por qué combinar allow-scripts y allow-same-origin en un iframe del mismo origen anula el sandbox?
    • El documento incrustado, con scripts y sin aislar, puede alcanzar el DOM del padre y eliminar él mismo el atributo sandbox
    • Los dos tokens son incompatibles y el navegador ignora todo el atributo
    • allow-same-origin desactiva automáticamente todos los demás tokens
  2. ¿Contra qué ataque protege rel=noopener?
    • El robo de cookies de sesión por la página abierta
    • La inyección de scripts en la página abierta
    • Tabnabbing: la página abierta usa window.opener para redirigir tu pestaña a un sitio de phishing
  3. ¿Qué directiva se ignora cuando la CSP se declara mediante <meta http-equiv>?
    • script-src
    • frame-ancestors
    • default-src
    • img-src