mediumCWE-770

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.

Vulnerable Code
// 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
  });
});
Secure Code
// 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

Node.jsPythonGo

Data Hogo detects this vulnerability automatically.

Scan Your Repo Free

Related Vulnerabilities