Tema 6

6. Cross-Site Scripting (XSS) y protección en aplicaciones modernas

Cross-Site Scripting, o XSS, aparece cuando una aplicación permite que contenido controlado por un atacante termine ejecutándose en el navegador de otro usuario. Es una vulnerabilidad especialmente peligrosa porque convierte al navegador legítimo en el vehículo del ataque.

Objetivo Comprender cómo se produce y se previene XSS
Enfoque Navegador, contexto de salida y defensa en capas
Resultado Diseñar salidas seguras en sitios y aplicaciones modernas

6.1 Introducción

Una aplicación web normalmente recibe datos, los procesa y luego los devuelve al navegador en forma de HTML, atributos, scripts, estilos o contenido dinámico. Si esa salida se construye sin controles adecuados, el navegador puede interpretar como código algo que en realidad proviene del atacante.

Ese es el núcleo de XSS: el sistema entrega al navegador contenido que no debería ejecutarse, pero termina ejecutándose dentro del contexto del sitio legítimo.

La gravedad de XSS no depende solo del script en sí. Depende del contexto donde se ejecuta: qué puede leer, qué sesión acompaña al usuario, qué acciones permite la aplicación y qué datos están disponibles en esa página.

6.2 Qué significa realmente XSS

Cross-Site Scripting no consiste simplemente en "inyectar JavaScript". Es una falla de confianza entre datos y contexto de salida. Ocurre cuando el sistema toma entrada no confiable y la inserta en una respuesta o en el DOM sin asegurarse de que sea tratada como dato y no como instrucción.

El navegador no distingue intenciones del desarrollador. Solo interpreta el resultado final que recibe. Si en ese resultado aparece una secuencia válida como código, la ejecutará según las reglas del documento y del origen.

XSS no es un problema de entrada solamente. Es, sobre todo, un problema de salida insegura en un contexto interpretado por el navegador.

6.3 Por qué XSS es tan peligroso

Cuando un XSS se explota, el código malicioso se ejecuta en el navegador de la víctima como si fuera parte del sitio legítimo. Eso puede permitir:

  • Robar o reutilizar información accesible en la página.
  • Realizar acciones en nombre del usuario.
  • Modificar la interfaz para engañar o capturar datos.
  • Redirigir a flujos fraudulentos.
  • Abusar de APIs internas accesibles desde el frontend.
  • Pivotear hacia otras vulnerabilidades del lado cliente.

Incluso si una cookie de sesión está marcada como `HttpOnly`, XSS sigue siendo grave porque el código puede usar el contexto autenticado del usuario para disparar acciones o leer datos expuestos en la interfaz.

6.4 Tipos principales de XSS

Existen tres categorías clásicas que ayudan a entender cómo llega el contenido malicioso al navegador.

  • XSS reflejado: el payload viaja en la solicitud y vuelve inmediatamente en la respuesta.
  • XSS almacenado: el payload se guarda en la aplicación y luego se entrega a otros usuarios.
  • DOM XSS: la vulnerabilidad ocurre en el navegador, cuando JavaScript manipula el DOM de forma insegura usando datos no confiables.

Estas categorías son útiles para estudiar el problema, aunque en sistemas reales pueden combinarse o solaparse.

6.5 XSS reflejado

En el XSS reflejado, el atacante logra que la aplicación devuelva en la respuesta contenido controlado por él, por ejemplo desde una URL, búsqueda o parámetro. La aplicación "refleja" ese dato sin tratarlo adecuadamente, y el navegador lo interpreta.

Este tipo de XSS suele requerir que la víctima visite un enlace especialmente preparado o interactúe con una solicitud manipulada. Por eso muchas campañas lo combinan con phishing o engaño social.

6.6 XSS almacenado

En el XSS almacenado, el atacante introduce el contenido malicioso en un lugar persistente de la aplicación, por ejemplo comentarios, perfiles, publicaciones, tickets o mensajes. Luego, cada usuario que visualiza ese contenido recibe la carga maliciosa desde el propio sitio legítimo.

Este tipo suele ser especialmente grave porque no depende de convencer individualmente a cada víctima. Una vez almacenado, el ataque puede propagarse a múltiples usuarios o incluso a administradores.

6.7 DOM XSS

En el DOM XSS, la vulnerabilidad no está necesariamente en la respuesta HTML generada por el servidor, sino en cómo el JavaScript del cliente procesa datos no confiables, por ejemplo desde la URL, `location`, `document.referrer`, `postMessage`, almacenamiento local o fragmentos del documento.

Si ese código inserta datos en el DOM mediante APIs inseguras, el navegador puede crear nodos interpretables y ejecutar contenido no previsto.

Esta variante es importante en aplicaciones modernas con frontend rico, donde gran parte de la lógica de interfaz vive en el navegador.

6.8 Contexto de salida: la idea más importante

La defensa contra XSS depende en gran medida del contexto donde se inserta el dato. No existe una única codificación universal que sirva para todo. El navegador interpreta de manera distinta un valor según si está en:

  • Contenido HTML.
  • Atributos HTML.
  • JavaScript embebido.
  • CSS.
  • URLs.

Por eso escapar salida correctamente significa aplicar la codificación adecuada para el contexto exacto. Un escape correcto para HTML puede ser insuficiente o incorrecto para un atributo o un script.

6.9 HTML, atributos y script: no es todo lo mismo

Contexto Riesgo Defensa principal
Texto HTML Interpretación como etiquetas o entidades Escape HTML por contexto
Atributos HTML Ruptura del atributo o inserción de eventos Escape adecuado y evitar atributos inseguros
JavaScript embebido Construcción de código ejecutable Evitar insertar datos en script inline
URLs Esquemas peligrosos o redirecciones indebidas Validar protocolos y codificar contexto URL
DOM dinámico Inserción insegura vía APIs del navegador Usar sinks seguros y evitar HTML arbitrario

6.10 Fuentes y sinks en DOM XSS

En análisis de DOM XSS suele hablarse de fuentes y sinks.

  • Fuente: lugar de donde proviene el dato no confiable, como la URL, `location.search`, `location.hash`, mensajes o almacenamiento local.
  • Sink: lugar donde ese dato se inserta o interpreta de manera peligrosa, por ejemplo APIs que generan HTML o ejecutan código.

El riesgo aparece cuando una fuente no confiable llega a un sink peligroso sin validación, sanitización o transformación segura.

6.11 Impacto real de XSS

Dependiendo del contexto de la aplicación, un XSS puede producir distintos impactos:

  • Robo de información visible en la página.
  • Captura de credenciales mediante formularios falsos.
  • Cambios en configuraciones o perfil del usuario.
  • Acciones administrativas disparadas desde una cuenta privilegiada.
  • Distribución de contenido malicioso a otros usuarios.
  • Compromiso reputacional por modificación de interfaz.

Un XSS en un panel de administración suele ser mucho más grave que uno en una página pública sin sesión ni datos sensibles.

6.12 XSS y sesiones

Durante mucho tiempo se asoció XSS principalmente al robo de cookies. Aunque eso sigue siendo relevante en ciertos escenarios, hoy el impacto es más amplio. Aun cuando las cookies estén protegidas con `HttpOnly`, el atacante puede utilizar el contexto de la sesión activa para enviar solicitudes, leer datos renderizados o modificar la interfaz.

Por eso marcar cookies como `HttpOnly` ayuda, pero no resuelve por sí solo el problema de XSS.

6.13 Escapar salida correctamente

La defensa técnica más importante contra XSS del lado servidor es escapar la salida según el contexto donde se inserta el dato. El objetivo es que el navegador interprete ese valor como contenido literal y no como estructura ejecutable.

Esto implica:

  • No construir HTML manualmente con concatenación insegura.
  • Usar mecanismos de template que escapen por defecto.
  • Evitar insertar datos en JavaScript inline.
  • Aplicar encoding específico según HTML, atributo, URL o script.

6.14 Sanitización cuando se permite HTML

Hay casos donde el negocio necesita aceptar contenido enriquecido, por ejemplo descripciones con formato, comentarios avanzados o editores WYSIWYG. En esos escenarios no alcanza con escape total, porque justamente se quiere permitir una parte limitada del markup.

Allí se necesita una sanitización robusta basada en una política clara de qué etiquetas, atributos y comportamientos están permitidos. Esta tarea debe hacerse con herramientas específicas y bien mantenidas. Hacerlo manualmente suele ser frágil.

Si el negocio no necesita HTML, la opción más segura suele ser no aceptarlo. Cuanto menos markup interpretable entre al sistema, menor superficie para XSS.

6.15 Frameworks modernos y autoescape

Muchos frameworks modernos de frontend y backend escapan contenido por defecto en contextos comunes. Esto reduce bastante el riesgo, pero no autoriza a bajar la guardia. La vulnerabilidad puede reaparecer cuando:

  • Se desactiva el escape automático.
  • Se usan APIs que insertan HTML crudo.
  • Se interpolan datos en contextos no cubiertos por el framework.
  • Se consume contenido enriquecido de terceros sin sanitización.

La herramienta ayuda, pero la comprensión del riesgo sigue siendo necesaria.

6.16 Content Security Policy

Content Security Policy, o CSP, es una cabecera de seguridad que ayuda a restringir qué scripts, estilos y recursos puede cargar o ejecutar una página. Bien configurada, puede mitigar ciertos escenarios de XSS o al menos dificultar su explotación.

Sin embargo, CSP no reemplaza el escape de salida ni la sanitización. Se considera una capa adicional de defensa, útil especialmente para limitar ejecución de scripts inline o cargas desde orígenes no esperados.

6.17 APIs del DOM más seguras y más peligrosas

En aplicaciones con JavaScript del lado cliente, la elección de APIs del DOM importa mucho. Algunas operaciones insertan texto literal y otras insertan HTML interpretable. Desde la seguridad, conviene preferir aquellas que tratan el contenido como texto cuando el negocio no requiere markup.

La regla práctica es sencilla: si solo necesitas mostrar texto, usa mecanismos que inserten texto. Reservar inserción de HTML para casos estrictamente necesarios reduce muchísimo el riesgo.

6.18 Errores frecuentes que introducen XSS

  • Renderizar directamente contenido proveniente del usuario.
  • Construir HTML concatenando strings.
  • Confiar en que un input "ya fue validado".
  • Permitir HTML enriquecido sin sanitización robusta.
  • Insertar datos en scripts inline o atributos de eventos.
  • Usar sinks peligrosos en el DOM con datos de la URL o del almacenamiento local.

6.19 Ejemplo conceptual de riesgo

Imaginemos un sistema de soporte donde los usuarios pueden enviar tickets y los administradores los leen desde un panel interno. Si el campo de mensaje acepta contenido que luego se renderiza sin control, un atacante podría insertar un payload que se ejecute cuando el operador abra el ticket.

En ese caso el XSS no afecta solo al usuario que escribió el mensaje, sino al operador o administrador que tiene más privilegios. El impacto cambia por completo según quién consuma el contenido vulnerable.

6.20 Defensa en profundidad contra XSS

Una estrategia sólida suele combinar varias capas:

  • Validar y restringir entradas según necesidad real.
  • Escapar salida por contexto.
  • Sanitizar HTML permitido con herramientas robustas.
  • Preferir APIs seguras del DOM.
  • Aplicar `HttpOnly` y `Secure` en cookies sensibles.
  • Agregar CSP como capa complementaria.
  • Revisar especialmente paneles administrativos y contenido compartido entre usuarios.

6.21 Qué debes recordar de este tema

  • XSS ocurre cuando contenido no confiable termina ejecutándose en el navegador dentro del contexto del sitio legítimo.
  • Las variantes principales son reflejado, almacenado y DOM XSS.
  • La defensa clave es tratar la salida según su contexto exacto.
  • Escapar salida, sanitizar HTML permitido y usar APIs seguras del DOM son medidas centrales.
  • CSP y cookies `HttpOnly` ayudan, pero no reemplazan la corrección del problema de base.

6.22 Conclusión

XSS es una vulnerabilidad especialmente importante porque ataca la confianza entre aplicación y navegador. Cuando una salida se construye sin cuidado, el código del atacante puede ejecutarse con los privilegios y el contexto del sitio real, afectando directamente a usuarios y administradores.

En el próximo tema estudiaremos `CSRF`, otra vulnerabilidad del lado navegador, pero con una lógica distinta: no busca ejecutar código en la víctima, sino inducir su navegador autenticado a realizar acciones no deseadas.