Cross-Site Scripting (XSS)
Publicado el 7 de diciembre de 2025
Introducción
El Cross-Site Scripting (XSS) sigue siendo una de las vulnerabilidades más comunes y peligrosas en aplicaciones web. Según el OWASP Top 10, las vulnerabilidades XSS continúan afectando a innumerables sitios y aplicaciones, lo que puede exponer a los usuarios a robo de datos, secuestro de sesión y distribución de malware.
¿Sabías qué?
XSS ha aparecido de forma constante en el OWASP Top 10 desde su creación, lo que demuestra la persistencia de esta clase de vulnerabilidades a pesar del aumento en la concientización.
Esta guía te ayudará a entender cómo identificar vulnerabilidades XSS durante una revisión de código, explicar cómo los atacantes explotan estas debilidades y darte técnicas prácticas para prevenirlas.
¿Qué es XSS?
Cross-Site Scripting (XSS) es una vulnerabilidad que permite a un atacante inyectar scripts maliciosos en páginas web que son vistas por otros usuarios.
Estos scripts se ejecutan en el navegador de la víctima y pueden acceder a cookies, tokens de sesión y otra información sensible que el navegador conserva para ese sitio.
Los ataques XSS ocurren cuando una aplicación incluye datos no confiables dentro de una página web sin aplicar una validación o un escape adecuado.
Tipos de vulnerabilidades XSS
| Tipo | Descripción | Vector de ataque |
|---|---|---|
| XSS Reflejado | El script se refleja desde el servidor web, por ejemplo, en resultados de búsqueda o mensajes de error. | Parámetros en la URL, campos de formularios |
| XSS Almacenado | El script malicioso se almacena en el servidor objetivo, como en una base de datos. | Comentarios, perfiles de usuario, foros |
| XSS basado en DOM | La vulnerabilidad existe en el código del lado del cliente en lugar del lado del servidor. | Fragmentos de URL, almacenamiento del lado del cliente |
XSS Reflejado
XSS Almacenado
XSS basado en DOM
¿Cuál de las siguientes opciones describe mejor el riesgo principal de las vulnerabilidades XSS?
Sources & Sinks
Source
Un source es cualquier propiedad, función o punto del código desde donde se obtiene información que puede ser controlada por un atacante. Los sources son el origen del flujo de datos no confiables.
Ejemplos típicos:
location.search(lee parámetros de la URL)document.referrer(lee la URL de referencia)document.cookie(accede a cookies manipulables por el usuario)- Mensajes recibidos mediante
postMessage - Valores almacenados en
localStorageosessionStorage
Cualquier mecanismo que permita que un atacante influya en el valor leído por el código puede considerarse un source.
Sink
Un sink es una función, propiedad u operación que puede producir efectos peligrosos si recibe datos controlados por un atacante. Los sinks son el punto donde el flujo de datos se vuelve riesgoso.
Ejemplos típicos de sinks:
-
Ejecución de JavaScript
eval()setTimeout()cuando recibe una cadenanew Function()
-
Manipulación del DOM con HTML
element.innerHTMLelement.outerHTMLdocument.write()
-
Atributos que permiten ejecutar código o cargar recursos
element.srcelement.hrefsi acepta esquemas comojavascript:
Un sink se convierte en una vulnerabilidad cuando recibe datos que provienen de un source sin validación ni saneamiento.
Relación entre source y sink
- El source proporciona el dato que puede estar bajo control del atacante.
- El sink es la operación peligrosa que se ejecuta con ese dato.
- Entre ambos puede existir o no un proceso de validación o sanitización.
- Si el dato fluye de un source a un sink sin protección, existe una vulnerabilidad.
Sinks de XSS comunes
JavaScript (NodeJS y navegador)
// DOM manipulation sinks
element.innerHTML = userInput;
element.outerHTML = userInput;
element.insertAdjacentHTML('beforeend', userInput);
document.write(userInput);
document.writeln(userInput);
// JavaScript execution sinks
eval(userInput);
setTimeout(userInput, 100);
setInterval(userInput, 100);
new Function(userInput);
// HTML attribute sinks
element.setAttribute('src', userInput);
element.setAttribute('href', userInput);
element.setAttribute('data', userInput);
element.src = userInput;
element.href = userInput; // cuidado con "javascript:"
element.onclick = userInput; // también cualquier on<Event>
Java (Servlets / JSP)
// HTML output sinks
out.print(userInput);
out.write(userInput);
response.getWriter().write(userInput);
// Attribute sinks (en etiquetas JSP o HTML manual)
"<a href=\"" + userInput + "\">enlace</a>"
"<img src=\"" + userInput + "\"/>"
// JavaScript execution via templates or inline
"<script>" + userInput + "</script>"
Python (Flask, Django sin escape)
# HTML output sinks
return userInput
response = make_response(userInput)
# Template injection (Jinja2/Django con autoescape desactivado)
{{ userInput|safe }}
{{ userInput|escape(False) }}
# Attribute injection
"<a href='{}'>".format(userInput)
f"<img src='{userInput}'>"
PHP
// Output sinks
echo $_GET['param'];
print $_POST['data'];
// HTML embedding
echo "<div>" . $_REQUEST['name'] . "</div>";
printf("<a href='%s'>", $_GET['link']);
// JavaScript injection
echo "<script>" . $_GET['code'] . "</script>";
C# (.NET - MVC, Razor)
// HTML output without encoding
@Html.Raw(userInput)
Response.Write(userInput);
LiteralControl.Text = userInput;
// HTML attribute injection
"<img src='" + userInput + "' />"
"<a href='" + userInput + "'>link</a>"
// JavaScript embedding
"<script>" + userInput + "</script>"
Rastreo del Flujo de Datos (Tracing Data Flow)
Para encontrar vulnerabilidades de XSS durante una revisión de código, debes revisar cómo fluye la información desde sources hasta sinks. Esto implica seguir la entrada del usuario conforme se mueve a través de la aplicación.
Análisis de Contaminación (Taint Analysis)
Una técnica llamada taint analysis permite rastrear entrada potencialmente peligrosa dentro de la base de código. Las herramientas modernas de análisis estático (SAST) pueden automatizar este proceso siguiendo variables derivadas de datos controlados por el usuario.
Preguntas clave en una revisión de código:
- ¿De dónde proviene la entrada del usuario?
- ¿Cómo se transforma esa entrada a medida que fluye por el código?
- ¿Se valida o sanitiza correctamente antes de llegar a un sink?
- ¿Existen bypasses o casos límite que podrían evadir las protecciones?
Las aplicaciones modernas a menudo tienen flujos de datos complejos que abarcan múltiples archivos, funciones e incluso servicios. Herramientas como SAST (Static Application Security Testing) pueden ayudar a identificar flujos potenciales desde sources hacia sinks.
Técnicas de Prevención
Prevenir XSS requiere combinar buenas prácticas de desarrollo con controles de seguridad.
Defensa en Profundidad (Defense in Depth)
La estrategia más efectiva para prevenir XSS combina múltiples capas de defensa. Ningún mecanismo individual es infalible.
Incluye:
- Validación de entrada: validar contra listas blancas de valores o patrones permitidos.
- Codificación de salida (Output Encoding): codificar caracteres especiales antes de insertar datos en contextos HTML, JavaScript, CSS o URL.
- Content Security Policy (CSP): usar cabeceras HTTP para restringir qué scripts pueden ejecutarse.
- APIs seguras: preferir APIs que no interpreten HTML/JS, como
textContenten lugar deinnerHTML. - Librerías de sanitización: usar librerías probadas para limpiar HTML o contenido del usuario.
Codificación Sensible al Contexto (Context-Sensitive Encoding)
// HTML context encoding
const safeHtml = escapeHtml(userInput);
element.innerHTML = safeHtml;
// JavaScript context encoding
const safeJs = JSON.stringify(userInput);
element.onclick = function () { alert(safeJs); };
// URL context encoding
const safeUrl = encodeURIComponent(userInput);
element.href = 'https://example.com/search?q=' + safeUrl;
// CSS context encoding
const safeCss = escapeCss(userInput);
element.style = 'color: ' + safeCss;
Corregir el Código Vulnerable
function showProfile(username) {
// Display the username in the profile section
document.getElementById('profile-name').innerHTML = username;
}
¿Cuál es la mejor solución?
Cuestionario: Métodos de Prevención de XSS
¿Qué vulnerabilidad ayuda a mitigar Content Security Policy (CSP)?
¿Qué vulnerabilidad ayuda a mitigar el uso de cookies con bandera HttpOnly?
¿Qué vulnerabilidad ayuda a mitigar la sanitización HTML?
¿Qué vulnerabilidad ayuda a mitigar el JavaScript encoding?
Recomendaciones por Framework
Los frameworks modernos proveen protecciones integradas contra XSS, pero solo funcionan si se usan correctamente.
| Framework | Patrones seguros | Patrones inseguros (sinks) |
|---|---|---|
| React | JSX con {} (autoescape) | dangerouslySetInnerHTML |
| Angular | Interpolación {{ }} | [innerHTML], bypassSecurityTrustHtml |
| Vue | {{ }}, v-text | v-html |
| Next.js | JSX / Server Components | dangerouslySetInnerHTML |
| Express | Motores de plantilla con autoescape | Construir HTML manualmente en res.send() |
Consejos rápidos:
- React: usar JSX; evitar
dangerouslySetInnerHTML. - Angular: usar property binding seguro; evitar
innerHTML. - Vue: usar
v-text;v-htmlsolo con contenido sanitizado. - Node/Express: siempre usar motores de plantillas con autoescape (Pug/EJS/Handlebars).
Desafío interactivo
¿Cuál de estos fragmentos React es vulnerable a XSS?
Casos Reales
Incluso grandes empresas han sufrido vulnerabilidades XSS.
Caso: Twitter — XSS(2019)
- Los atacantes podían publicar tweets con JavaScript embebido.
- Causa: sanitización insuficiente de contenido.
Lección: incluso plataformas maduras necesitan auditorías constantes y sanitización robusta.
Recursos Útiles
Artículos para responder cuestionarios
Análisis estático (SAST):
Escáneres dinámicos (DAST):
Sanitización:
Protección en tiempo de ejecución:
Recursos para aprender más sobre XSS:
- OWASP XSS Prevention Cheat Sheet
- PortSwigger Web Security Academy - XSS
- Google’s XSS Game
- HackerOne Disclosed XSS Reports
Ejercicios
Ejercicio 1: Kotlin & Spring Boot XSS
Esta aplicación permite a los usuarios previsualizar sus publicaciones formateadas con Markdown antes de publicarlas. Sin embargo, un atacante ha descubierto que puede ejecutar JavaScript arbitrario en la vista de previsualización.
Analiza la arquitectura completa y encuentra la línea exacta en el servicio de Markdown que introduce esta vulnerabilidad.