WebSocket Flooding (No Rate Limit)
A WebSocket server without message rate limiting allows a single client to send thousands of messages per second, exhausting server resources, degrading performance for all users, and potentially causing a denial-of-service.
How It Works
WebSocket connections are persistent and bidirectional, meaning a client can send messages at any rate without needing to establish new connections. Unlike HTTP endpoints where each request has connection overhead, WebSocket messages are extremely lightweight. Without rate limiting, a single malicious client can flood the server with thousands of messages per second. If each message triggers server-side processing (database queries, broadcasts to other clients, API calls), the server's CPU, memory, and I/O are exhausted. This degrades performance for all connected users and can crash the server entirely. The attack is trivial to execute: a simple JavaScript loop sending messages as fast as possible is enough to overwhelm most unprotected WebSocket servers.
// BAD: no rate limiting on WebSocket messages
const WebSocket = require('ws');
const server = new WebSocket.Server({ port: 8080 });
server.on('connection', (ws) => {
ws.on('message', async (data) => {
const msg = JSON.parse(data);
// Every message triggers a database write and broadcast
await db.insert('messages', msg);
server.clients.forEach((client) => {
if (client.readyState === WebSocket.OPEN) {
client.send(JSON.stringify(msg)); // broadcast to all
}
});
// Attacker sends 10,000 msgs/sec -> 10,000 DB writes + broadcasts/sec
});
});// GOOD: rate limiting with token bucket per connection
const WebSocket = require('ws');
const server = new WebSocket.Server({ port: 8080 });
function createRateLimiter(maxTokens, refillRate) {
let tokens = maxTokens;
setInterval(() => { tokens = Math.min(maxTokens, tokens + refillRate); }, 1000);
return () => { if (tokens > 0) { tokens--; return true; } return false; };
}
server.on('connection', (ws) => {
const allowMessage = createRateLimiter(10, 5); // 10 burst, 5/sec refill
let warnings = 0;
ws.on('message', async (data) => {
if (!allowMessage()) {
warnings++;
if (warnings > 3) { ws.close(1008, 'Rate limit exceeded'); return; }
ws.send(JSON.stringify({ error: 'Too many messages, slow down' }));
return;
}
const msg = JSON.parse(data);
await db.insert('messages', msg);
broadcast(server, msg);
});
});Real-World Example
In 2020, researchers demonstrated WebSocket flooding attacks against several popular real-time chat platforms built on Socket.io, showing that a single connection could degrade service for thousands of concurrent users. The attacks exploited the lack of per-connection rate limiting. One notable case involved a gaming platform where attackers flooded the WebSocket server to cause lag and gain competitive advantage, affecting thousands of players during peak hours.
How to Prevent It
- Implement per-connection message rate limiting using a token bucket or sliding window algorithm that limits messages to a reasonable rate (e.g., 10-50/second)
- Set maximum message size limits on the WebSocket server to prevent large payload attacks (use maxPayload option in ws library)
- Disconnect clients that repeatedly exceed rate limits with a warning system (warn, then close with code 1008)
- Monitor WebSocket connections for anomalous message rates and implement server-level connection limits to cap total concurrent WebSocket connections
Affected Technologies
Data Hogo detects this vulnerability automatically.
Scan Your Repo Free