WebSocket API Security Vulnerabilities and Authentication Bypass

High Risk API Security
websocketreal-timeauthenticationcross-sitemessage-injectionorigin-validationrate-limiting

What it is

A high-severity vulnerability where WebSocket APIs lack proper security controls including authentication bypass, missing origin validation, inadequate message validation, and insufficient rate limiting. WebSocket connections can be exploited for real-time attacks, cross-site WebSocket hijacking, message injection, and unauthorized access to real-time data streams and communication channels.

// VULNERABLE: WebSocket server with multiple security issues const WebSocket = require('ws'); const express = require('express'); const http = require('http'); const app = express(); const server = http.createServer(app); // PROBLEM: No security controls whatsoever const wss = new WebSocket.Server({ server }); wss.on('connection', (ws, req) => { // DANGEROUS: No authentication or origin validation console.log('Connection established'); ws.on('message', (message) => { try { const data = JSON.parse(message); // VULNERABLE: No input validation or rate limiting switch (data.type) { case 'chat': // DANGEROUS: Broadcasting unvalidated content broadcast({ type: 'chat', user: data.user, // User-controlled message: data.message, // No XSS protection channel: data.channel }); break; case 'admin_command': // EXTREMELY DANGEROUS: Admin commands without auth if (data.command === 'eval') { eval(data.code); // Code execution! } break; case 'file_upload': // DANGEROUS: File upload without validation const fs = require('fs'); fs.writeFileSync(data.filename, data.content); break; } } catch (error) { // PROBLEM: Exposing error details ws.send(JSON.stringify({ error: error.message, stack: error.stack })); } }); }); function broadcast(message) { // PROBLEM: No authorization checks for broadcasting wss.clients.forEach(client => { if (client.readyState === WebSocket.OPEN) { client.send(JSON.stringify(message)); } }); } server.listen(8080); // Attack scenarios: // 1. Cross-site WebSocket hijacking // 2. XSS injection through chat messages // 3. Code execution via admin commands // 4. File system manipulation // 5. Unlimited connections and message flooding
// SECURE: Comprehensive WebSocket security implementation const WebSocket = require('ws'); const express = require('express'); const http = require('http'); const jwt = require('jsonwebtoken'); const crypto = require('crypto'); const DOMPurify = require('isomorphic-dompurify'); const rateLimit = require('express-rate-limit'); const app = express(); const server = http.createServer(app); // Security configuration const config = { allowedOrigins: [ 'https://app.example.com', 'https://admin.example.com' ], jwtSecret: process.env.JWT_SECRET, maxConnections: 1000, maxConnectionsPerIP: 10, maxMessageRate: 60, // per minute maxMessageSize: 10240 // 10KB }; // Resource management class SecureWebSocketManager { constructor() { this.connections = new Map(); this.channels = new Map(); this.ipConnections = new Map(); this.rateLimits = new Map(); this.csrfTokens = new Map(); // Cleanup interval setInterval(() => this.cleanup(), 60000); } verifyClient(info) { const origin = info.origin; const ip = info.req.socket.remoteAddress; // Origin validation if (!origin || !config.allowedOrigins.includes(origin)) { this.logSecurityViolation('Invalid origin', { origin, ip }); return false; } // IP connection limits const ipCount = this.ipConnections.get(ip)?.size || 0; if (ipCount >= config.maxConnectionsPerIP) { this.logSecurityViolation('IP connection limit', { ip, count: ipCount }); return false; } // Global connection limit if (this.connections.size >= config.maxConnections) { return false; } return true; } async authenticateConnection(req) { // Extract token from query or header const token = new URL(req.url, 'http://localhost').searchParams.get('token') || req.headers.authorization?.replace('Bearer ', ''); if (!token) { throw new Error('Authentication token required'); } try { const decoded = jwt.verify(token, config.jwtSecret, { algorithms: ['HS256'], maxAge: '1h' }); // Verify user exists and is active const user = await this.getUserById(decoded.userId); if (!user || !user.isActive) { throw new Error('User not found or inactive'); } return user; } catch (error) { throw new Error(`Authentication failed: ${error.message}`); } } registerConnection(ws, req, user) { const connectionId = crypto.randomUUID(); const ip = req.socket.remoteAddress; const connection = { id: connectionId, ws, user, ip, connectedAt: new Date(), lastActivity: new Date(), subscriptions: new Set(), messageCount: 0, rateLimit: { messages: { count: 0, resetTime: Date.now() + 60000 }, subscriptions: { count: 0, resetTime: Date.now() + 300000 } } }; this.connections.set(connectionId, connection); // Track IP connections if (!this.ipConnections.has(ip)) { this.ipConnections.set(ip, new Set()); } this.ipConnections.get(ip).add(connectionId); return connectionId; } validateMessage(rawMessage, connection) { // Size validation if (rawMessage.length > config.maxMessageSize) { throw new Error('Message too large'); } // Rate limiting if (!this.checkRateLimit(connection)) { throw new Error('Rate limit exceeded'); } // Parse and validate JSON const message = JSON.parse(rawMessage); if (!message.type || typeof message.type !== 'string') { throw new Error('Invalid message format'); } // Security validation this.validateSecurity(message); return this.sanitizeMessage(message); } checkRateLimit(connection) { const now = Date.now(); const limit = connection.rateLimit.messages; if (now > limit.resetTime) { limit.count = 0; limit.resetTime = now + 60000; } if (limit.count >= config.maxMessageRate) { return false; } limit.count++; return true; } validateSecurity(message) { const messageStr = JSON.stringify(message); // XSS/injection patterns const dangerousPatterns = [ /]*>/i, /javascript:/i, /on\w+\s*=/i, /eval\s*\(/i, /expression\s*\(/i ]; for (const pattern of dangerousPatterns) { if (pattern.test(messageStr)) { throw new Error('Potentially dangerous content detected'); } } } sanitizeMessage(message) { const sanitized = { ...message }; for (const [key, value] of Object.entries(sanitized)) { if (typeof value === 'string') { sanitized[key] = DOMPurify.sanitize(value, { ALLOWED_TAGS: [], ALLOWED_ATTR: [], KEEP_CONTENT: true }).trim(); } } return sanitized; } async handleMessage(connectionId, message) { const connection = this.connections.get(connectionId); if (!connection) return; connection.lastActivity = new Date(); connection.messageCount++; switch (message.type) { case 'subscribe': await this.handleSubscription(connectionId, message); break; case 'chat': await this.handleChatMessage(connectionId, message); break; case 'admin_action': await this.handleAdminAction(connectionId, message); break; default: this.sendError(connectionId, 'Unknown message type'); } } async handleSubscription(connectionId, message) { const connection = this.connections.get(connectionId); const channel = message.channel; // Validate channel access if (!await this.canAccessChannel(connection.user, channel)) { this.sendError(connectionId, 'Access denied to channel'); return; } // Subscription limits if (connection.subscriptions.size >= 20) { this.sendError(connectionId, 'Too many subscriptions'); return; } // Add to channel if (!this.channels.has(channel)) { this.channels.set(channel, new Set()); } this.channels.get(channel).add(connectionId); connection.subscriptions.add(channel); this.sendToConnection(connectionId, { type: 'subscribed', channel, timestamp: new Date().toISOString() }); } async handleChatMessage(connectionId, message) { const connection = this.connections.get(connectionId); const channel = message.channel; // Validate channel access if (!await this.canAccessChannel(connection.user, channel)) { this.sendError(connectionId, 'Cannot send to this channel'); return; } // Additional message validation if (!message.content || message.content.length > 1000) { this.sendError(connectionId, 'Invalid message content'); return; } const chatMessage = { type: 'chat', channel, user: { id: connection.user.id, username: connection.user.username }, content: message.content, // Already sanitized timestamp: new Date().toISOString() }; this.broadcastToChannel(channel, chatMessage); } async handleAdminAction(connectionId, message) { const connection = this.connections.get(connectionId); // Strict admin validation if (connection.user.role !== 'admin') { this.sendError(connectionId, 'Admin privileges required'); this.logSecurityViolation('Unauthorized admin attempt', { userId: connection.user.id, action: message.action }); return; } // Limited admin actions const allowedActions = ['kick_user', 'mute_user', 'delete_message']; if (!allowedActions.includes(message.action)) { this.sendError(connectionId, 'Invalid admin action'); return; } // Process safe admin action await this.processAdminAction(connection.user, message); } async canAccessChannel(user, channel) { const permissions = { 'public.*': () => true, 'user.*': () => user && user.role === 'user', 'admin.*': () => user && user.role === 'admin' }; for (const [pattern, check] of Object.entries(permissions)) { if (this.matchPattern(channel, pattern)) { return check(); } } return false; } broadcastToChannel(channel, message) { const channelConnections = this.channels.get(channel); if (!channelConnections) return; const messageStr = JSON.stringify(message); for (const connectionId of channelConnections) { const connection = this.connections.get(connectionId); if (connection && connection.ws.readyState === WebSocket.OPEN) { connection.ws.send(messageStr); } } } sendToConnection(connectionId, message) { const connection = this.connections.get(connectionId); if (connection && connection.ws.readyState === WebSocket.OPEN) { connection.ws.send(JSON.stringify(message)); } } sendError(connectionId, message) { this.sendToConnection(connectionId, { type: 'error', error: message, timestamp: new Date().toISOString() }); } cleanup() { const now = Date.now(); const timeout = 300000; // 5 minutes for (const [connectionId, connection] of this.connections.entries()) { if (now - connection.lastActivity.getTime() > timeout) { this.disconnectConnection(connectionId, 'Inactivity timeout'); } } // Clean expired CSRF tokens for (const [token, data] of this.csrfTokens.entries()) { if (now > data.expiresAt) { this.csrfTokens.delete(token); } } } disconnectConnection(connectionId, reason) { const connection = this.connections.get(connectionId); if (!connection) return; try { connection.ws.close(1000, reason); } catch (error) { // Connection already closed } this.unregisterConnection(connectionId); } unregisterConnection(connectionId) { const connection = this.connections.get(connectionId); if (!connection) return; // Remove from channels for (const channel of connection.subscriptions) { const channelConnections = this.channels.get(channel); if (channelConnections) { channelConnections.delete(connectionId); if (channelConnections.size === 0) { this.channels.delete(channel); } } } // Remove from IP tracking const ipSet = this.ipConnections.get(connection.ip); if (ipSet) { ipSet.delete(connectionId); if (ipSet.size === 0) { this.ipConnections.delete(connection.ip); } } this.connections.delete(connectionId); } logSecurityViolation(message, details) { console.warn('WebSocket security violation:', { message, ...details, timestamp: new Date().toISOString() }); } matchPattern(str, pattern) { const regex = new RegExp(pattern.replace('*', '.*')); return regex.test(str); } async getUserById(id) { // Mock user lookup - replace with actual database call return { id, username: `user_${id}`, role: 'user', isActive: true }; } } // Initialize secure WebSocket manager const wsManager = new SecureWebSocketManager(); // Create secure WebSocket server const wss = new WebSocket.Server({ server, verifyClient: (info) => wsManager.verifyClient(info) }); wss.on('connection', async (ws, req) => { try { // Authenticate connection const user = await wsManager.authenticateConnection(req); // Register connection const connectionId = wsManager.registerConnection(ws, req, user); // Send authentication confirmation ws.send(JSON.stringify({ type: 'authenticated', user: { id: user.id, username: user.username, role: user.role } })); // Message handler ws.on('message', async (rawMessage) => { try { const connection = wsManager.connections.get(connectionId); const message = wsManager.validateMessage(rawMessage, connection); await wsManager.handleMessage(connectionId, message); } catch (error) { wsManager.sendError(connectionId, error.message); } }); // Cleanup on disconnect ws.on('close', () => { wsManager.unregisterConnection(connectionId); }); ws.on('error', (error) => { console.error('WebSocket error:', error); wsManager.unregisterConnection(connectionId); }); } catch (error) { console.error('WebSocket authentication failed:', error); ws.close(1008, 'Authentication failed'); } }); // Rate limiting for WebSocket endpoint app.use('/ws', rateLimit({ windowMs: 60000, max: 10, message: 'Too many WebSocket connection attempts' })); // CSRF token endpoint app.get('/api/websocket/token', (req, res) => { const origin = req.headers.origin; if (!config.allowedOrigins.includes(origin)) { return res.status(403).json({ error: 'Origin not allowed' }); } const token = jwt.sign( { userId: req.user?.id || 'anonymous' }, config.jwtSecret, { expiresIn: '1h' } ); res.json({ token, websocketUrl: `wss://your-domain.com/ws?token=${token}` }); }); server.listen(8080, () => { console.log('Secure WebSocket server running on port 8080'); });

💡 Why This Fix Works

The secure implementation addresses all major WebSocket vulnerabilities through comprehensive authentication and authorization, origin validation with CSRF protection, input validation and sanitization, rate limiting and resource management, secure message handling, and proper error handling. It prevents cross-site WebSocket hijacking, message injection attacks, and resource exhaustion while maintaining security best practices for real-time communications.

Why it happens

WebSocket connections that don't properly authenticate users or validate authorization for real-time operations allow attackers to connect anonymously, access sensitive data streams, and perform unauthorized actions through WebSocket messages.

Root causes

Missing Authentication and Authorization in WebSocket Connections

WebSocket connections that don't properly authenticate users or validate authorization for real-time operations allow attackers to connect anonymously, access sensitive data streams, and perform unauthorized actions through WebSocket messages.

Preview example – JAVASCRIPT
// VULNERABLE: WebSocket server without authentication
const WebSocket = require('ws');
const express = require('express');
const http = require('http');

const app = express();
const server = http.createServer(app);

// PROBLEM: WebSocket server without authentication
const wss = new WebSocket.Server({ server });

wss.on('connection', (ws, req) => {
    // DANGEROUS: No authentication check
    console.log('New WebSocket connection');
    
    // PROBLEM: All users get access to all channels
    ws.on('message', (message) => {
        try {
            const data = JSON.parse(message);
            
            // VULNERABLE: No authorization checks
            switch (data.type) {
                case 'subscribe':
                    // DANGEROUS: Can subscribe to any channel
                    ws.channel = data.channel;
                    ws.send(JSON.stringify({
                        type: 'subscribed',
                        channel: data.channel
                    }));
                    break;
                    
                case 'chat_message':
                    // DANGEROUS: Can send messages to any channel
                    broadcastToChannel(data.channel, {
                        type: 'chat_message',
                        user: data.user,        // User-controlled
                        message: data.message,  // No validation
                        timestamp: Date.now()
                    });
                    break;
                    
                case 'admin_command':
                    // EXTREMELY DANGEROUS: Admin commands without auth
                    if (data.command === 'kick_user') {
                        kickUser(data.targetUser);
                    } else if (data.command === 'ban_user') {
                        banUser(data.targetUser);
                    }
                    break;
                    
                case 'get_user_data':
                    // DANGEROUS: Exposing user data without auth
                    const userData = getUserData(data.userId);
                    ws.send(JSON.stringify({
                        type: 'user_data',
                        data: userData  // Sensitive user information
                    }));
                    break;
            }
        } catch (error) {
            // PROBLEM: Error messages may leak information
            ws.send(JSON.stringify({
                type: 'error',
                message: error.message,
                stack: error.stack,
                data: data
            }));
        }
    });
    
    // PROBLEM: No connection limits or rate limiting
    ws.on('close', () => {
        console.log('WebSocket connection closed');
    });
});

function broadcastToChannel(channel, message) {
    wss.clients.forEach((client) => {
        // DANGEROUS: Broadcasting to all clients regardless of authorization
        if (client.readyState === WebSocket.OPEN && client.channel === channel) {
            client.send(JSON.stringify(message));
        }
    });
}

// Attack scenarios:
// 1. Connect without authentication
// 2. Subscribe to admin channels
// 3. Send fake messages as other users
// 4. Execute admin commands
// 5. Access sensitive user data
// 6. Flood connections without limits

Cross-Site WebSocket Hijacking (CSWSH) Through Missing Origin Validation

WebSocket servers that don't validate the Origin header allow malicious websites to establish WebSocket connections on behalf of authenticated users, enabling cross-site WebSocket hijacking attacks and unauthorized real-time communication.

Preview example – JAVASCRIPT
// VULNERABLE: No origin validation in WebSocket server
const WebSocket = require('ws');
const url = require('url');

// PROBLEM: WebSocket server accepts connections from any origin
const wss = new WebSocket.Server({
    port: 8080,
    // MISSING: Origin validation
    verifyClient: (info) => {
        // DANGEROUS: Always accepting connections
        return true;
    }
});

wss.on('connection', (ws, req) => {
    const origin = req.headers.origin;
    const userAgent = req.headers['user-agent'];
    
    // PROBLEM: No origin checking
    console.log(`Connection from origin: ${origin}`);
    
    // VULNERABLE: Extract authentication from cookies/headers without origin validation
    const cookies = parseCookies(req.headers.cookie || '');
    const sessionId = cookies.sessionId;
    
    if (sessionId) {
        // DANGEROUS: Using session without origin verification
        const user = getUserFromSession(sessionId);
        if (user) {
            ws.user = user;
            ws.send(JSON.stringify({
                type: 'authenticated',
                user: {
                    id: user.id,
                    username: user.username,
                    role: user.role
                }
            }));
        }
    }
    
    ws.on('message', (message) => {
        const data = JSON.parse(message);
        
        // VULNERABLE: Processing authenticated actions without origin check
        if (ws.user) {
            switch (data.type) {
                case 'get_private_messages':
                    // DANGEROUS: Sensitive data without origin validation
                    const messages = getPrivateMessages(ws.user.id);
                    ws.send(JSON.stringify({
                        type: 'private_messages',
                        messages: messages
                    }));
                    break;
                    
                case 'transfer_money':
                    // EXTREMELY DANGEROUS: Financial operations
                    if (ws.user.role === 'premium') {
                        transferMoney(ws.user.id, data.recipient, data.amount);
                        ws.send(JSON.stringify({
                            type: 'transfer_complete',
                            amount: data.amount,
                            recipient: data.recipient
                        }));
                    }
                    break;
                    
                case 'update_profile':
                    // DANGEROUS: Profile modifications
                    updateUserProfile(ws.user.id, data.profileData);
                    break;
            }
        }
    });
});

// VULNERABLE: Cookie parsing without security checks
function parseCookies(cookieHeader) {
    const cookies = {};
    cookieHeader.split(';').forEach(cookie => {
        const [name, value] = cookie.trim().split('=');
        cookies[name] = value;
    });
    return cookies;
}

// Attack example:
// Malicious website at evil.com can establish WebSocket connection
// and perform actions on behalf of authenticated user:

// <script>
// const ws = new WebSocket('ws://victim-app.com:8080');
// ws.onopen = () => {
//     // This will use the victim's session cookies
//     ws.send(JSON.stringify({
//         type: 'get_private_messages'
//     }));
//     
//     ws.send(JSON.stringify({
//         type: 'transfer_money',
//         recipient: 'attacker_account',
//         amount: 1000
//     }));
// };
// </script>

WebSocket Message Injection and Insufficient Input Validation

WebSocket applications that don't properly validate, sanitize, or rate-limit incoming messages are vulnerable to message injection attacks, including script injection in real-time chat applications, command injection, and message flooding attacks.

Preview example – JAVASCRIPT
// VULNERABLE: WebSocket chat application without input validation
const WebSocket = require('ws');
const express = require('express');
const fs = require('fs');

const wss = new WebSocket.Server({ port: 8080 });
const connectedUsers = new Map();
const chatHistory = [];

wss.on('connection', (ws, req) => {
    let user = null;
    
    ws.on('message', (message) => {
        try {
            const data = JSON.parse(message);
            
            switch (data.type) {
                case 'join':
                    // PROBLEM: No input validation on username
                    user = {
                        id: generateId(),
                        username: data.username,  // No sanitization
                        ws: ws
                    };
                    connectedUsers.set(ws, user);
                    
                    // DANGEROUS: Broadcasting user-controlled data
                    broadcast({
                        type: 'user_joined',
                        username: data.username,  // XSS vector
                        message: `${data.username} joined the chat`
                    });
                    break;
                    
                case 'chat':
                    if (!user) return;
                    
                    // PROBLEM: No message validation or sanitization
                    const chatMessage = {
                        type: 'chat',
                        username: user.username,
                        message: data.message,    // No XSS protection
                        timestamp: Date.now(),
                        userId: user.id
                    };
                    
                    // DANGEROUS: Storing unsanitized content
                    chatHistory.push(chatMessage);
                    
                    // PROBLEM: No rate limiting
                    broadcast(chatMessage);
                    break;
                    
                case 'private_message':
                    // VULNERABLE: No validation on recipient or message
                    const targetUser = findUserById(data.targetUserId);
                    if (targetUser) {
                        targetUser.ws.send(JSON.stringify({
                            type: 'private_message',
                            from: user.username,
                            message: data.message,  // Unsanitized
                            timestamp: Date.now()
                        }));
                    }
                    break;
                    
                case 'admin_command':
                    // EXTREMELY DANGEROUS: Command injection
                    if (data.command && user.username.includes('admin')) {
                        try {
                            // DANGEROUS: Executing user input
                            eval(data.command);
                            
                            // DANGEROUS: File system operations
                            if (data.command.startsWith('save_log')) {
                                const filename = data.filename || 'chat.log';
                                const content = data.content || JSON.stringify(chatHistory);
                                
                                // VULNERABLE: Path traversal
                                fs.writeFileSync(`./logs/${filename}`, content);
                            }
                            
                            ws.send(JSON.stringify({
                                type: 'command_result',
                                result: 'Command executed successfully'
                            }));
                        } catch (error) {
                            // PROBLEM: Error details exposed
                            ws.send(JSON.stringify({
                                type: 'command_error',
                                error: error.message,
                                stack: error.stack
                            }));
                        }
                    }
                    break;
                    
                case 'file_upload':
                    // DANGEROUS: File upload through WebSocket without validation
                    const fileData = Buffer.from(data.fileContent, 'base64');
                    const filename = data.filename;
                    
                    // VULNERABLE: No file type or size validation
                    fs.writeFileSync(`./uploads/${filename}`, fileData);
                    
                    broadcast({
                        type: 'file_shared',
                        filename: filename,
                        uploader: user.username,
                        size: fileData.length
                    });
                    break;
            }
        } catch (error) {
            // PROBLEM: Detailed error information
            ws.send(JSON.stringify({
                type: 'error',
                message: error.message,
                stack: error.stack,
                originalMessage: message
            }));
        }
    });
    
    ws.on('close', () => {
        if (user) {
            connectedUsers.delete(ws);
            broadcast({
                type: 'user_left',
                username: user.username,
                message: `${user.username} left the chat`
            });
        }
    });
});

function broadcast(message) {
    // PROBLEM: No message size limits
    const messageStr = JSON.stringify(message);
    
    wss.clients.forEach((client) => {
        if (client.readyState === WebSocket.OPEN) {
            client.send(messageStr);
        }
    });
}

// Attack scenarios:
// 1. XSS injection: username: "<script>alert('XSS')</script>"
// 2. Command injection: admin_command: "require('child_process').exec('rm -rf /')"
// 3. Path traversal: filename: "../../../etc/passwd"
// 4. Message flooding: Send thousands of messages rapidly
// 5. Large file uploads: Upload massive files to exhaust storage
// 6. JSON bomb: Send deeply nested JSON to cause parser issues

WebSocket Rate Limiting and Resource Exhaustion Vulnerabilities

WebSocket servers without proper rate limiting, connection limits, and resource management are vulnerable to denial of service attacks through connection flooding, message bombing, and resource exhaustion attacks that can overwhelm the server and degrade service for legitimate users.

Preview example – JAVASCRIPT
// VULNERABLE: WebSocket server without rate limiting or resource controls
const WebSocket = require('ws');
const EventEmitter = require('events');

// PROBLEM: No connection limits
const wss = new WebSocket.Server({
    port: 8080,
    maxPayload: undefined,  // DANGEROUS: No message size limit
    // MISSING: Connection limits, rate limiting
});

// PROBLEM: No tracking of resource usage
const gameRooms = new Map();
const userSessions = new Map();

wss.on('connection', (ws, req) => {
    // PROBLEM: No connection counting or limits
    console.log('New connection established');
    
    let messageCount = 0;
    let lastMessageTime = Date.now();
    
    ws.on('message', (message) => {
        // PROBLEM: No rate limiting per connection
        messageCount++;
        
        try {
            const data = JSON.parse(message);
            
            switch (data.type) {
                case 'create_room':
                    // VULNERABLE: Unlimited room creation
                    const roomId = generateRoomId();
                    const room = {
                        id: roomId,
                        name: data.roomName,
                        owner: ws,
                        players: [ws],
                        gameState: initializeGameState(),
                        messages: []
                    };
                    
                    // PROBLEM: No limit on number of rooms
                    gameRooms.set(roomId, room);
                    ws.roomId = roomId;
                    
                    ws.send(JSON.stringify({
                        type: 'room_created',
                        roomId: roomId
                    }));
                    break;
                    
                case 'join_room':
                    const targetRoom = gameRooms.get(data.roomId);
                    if (targetRoom) {
                        // PROBLEM: No limit on players per room
                        targetRoom.players.push(ws);
                        ws.roomId = data.roomId;
                        
                        // DANGEROUS: Broadcasting to all players without limits
                        broadcastToRoom(data.roomId, {
                            type: 'player_joined',
                            playerCount: targetRoom.players.length
                        });
                    }
                    break;
                    
                case 'game_action':
                    // PROBLEM: No validation of action frequency
                    if (ws.roomId) {
                        const room = gameRooms.get(ws.roomId);
                        if (room) {
                            // VULNERABLE: Processing unlimited game actions
                            processGameAction(room, ws, data.action);
                            
                            // DANGEROUS: Broadcasting every action
                            broadcastToRoom(ws.roomId, {
                                type: 'game_update',
                                gameState: room.gameState,  // Potentially large object
                                action: data.action
                            });
                        }
                    }
                    break;
                    
                case 'chat_message':
                    // PROBLEM: No message rate limiting
                    if (ws.roomId) {
                        const room = gameRooms.get(ws.roomId);
                        if (room) {
                            const chatMessage = {
                                user: data.username,
                                message: data.message,
                                timestamp: Date.now()
                            };
                            
                            // VULNERABLE: Unlimited message storage
                            room.messages.push(chatMessage);
                            
                            broadcastToRoom(ws.roomId, {
                                type: 'chat_message',
                                ...chatMessage
                            });
                        }
                    }
                    break;
                    
                case 'bulk_operation':
                    // EXTREMELY DANGEROUS: Processing bulk operations without limits
                    const operations = data.operations; // Could be thousands
                    
                    operations.forEach(operation => {
                        // PROBLEM: No limit on operation count
                        switch (operation.type) {
                            case 'create_object':
                                createGameObject(operation.data);
                                break;
                            case 'update_state':
                                updateGameState(operation.data);
                                break;
                            case 'send_notification':
                                sendNotification(operation.data);
                                break;
                        }
                    });
                    
                    ws.send(JSON.stringify({
                        type: 'bulk_complete',
                        processedCount: operations.length
                    }));
                    break;
                    
                case 'data_export':
                    // DANGEROUS: Unlimited data export
                    const exportData = {
                        rooms: Array.from(gameRooms.values()),
                        sessions: Array.from(userSessions.values()),
                        timestamp: Date.now()
                    };
                    
                    // PROBLEM: Sending potentially massive data
                    ws.send(JSON.stringify({
                        type: 'export_data',
                        data: exportData  // Could be gigabytes
                    }));
                    break;
            }
        } catch (error) {
            // PROBLEM: Error handling without rate limiting
            ws.send(JSON.stringify({
                type: 'error',
                message: error.message
            }));
        }
    });
    
    // PROBLEM: No cleanup of resources on disconnect
    ws.on('close', () => {
        console.log('Connection closed');
        // Missing: Remove from rooms, clean up resources
    });
    
    // PROBLEM: No error handling for connection issues
    ws.on('error', (error) => {
        console.error('WebSocket error:', error);
    });
});

function broadcastToRoom(roomId, message) {
    const room = gameRooms.get(roomId);
    if (room) {
        // PROBLEM: No limits on broadcast frequency or size
        const messageStr = JSON.stringify(message);
        
        room.players.forEach(player => {
            if (player.readyState === WebSocket.OPEN) {
                player.send(messageStr);
            }
        });
    }
}

// Attack scenarios:
// 1. Connection flooding: Open thousands of WebSocket connections
// 2. Message bombing: Send thousands of messages per second
// 3. Room creation spam: Create unlimited game rooms
// 4. Large message attacks: Send massive JSON payloads
// 5. Bulk operation abuse: Send operations with thousands of items
// 6. Data export DoS: Request massive data exports repeatedly
// 7. Resource exhaustion: Keep connections open without activity

Fixes

1

Implement Secure WebSocket Authentication and Authorization

Add comprehensive authentication and authorization controls to WebSocket connections, including token-based authentication, role-based access control, and secure session management for real-time operations.

View implementation – JAVASCRIPT
const WebSocket = require('ws');
const jwt = require('jsonwebtoken');
const url = require('url');
const crypto = require('crypto');

// Secure WebSocket server with authentication
class SecureWebSocketServer {
    constructor(options) {
        this.allowedOrigins = options.allowedOrigins || [];
        this.jwtSecret = options.jwtSecret;
        this.connections = new Map();
        this.channels = new Map();
        
        this.wss = new WebSocket.Server({
            ...options,
            verifyClient: this.verifyClient.bind(this)
        });
        
        this.wss.on('connection', this.handleConnection.bind(this));
    }
    
    verifyClient(info) {
        const origin = info.origin;
        const userAgent = info.req.headers['user-agent'];
        
        // Validate origin
        if (origin && !this.isOriginAllowed(origin)) {
            console.warn('WebSocket connection rejected - invalid origin:', {
                origin,
                ip: info.req.socket.remoteAddress,
                userAgent,
                timestamp: new Date().toISOString()
            });
            return false;
        }
        
        // Check for suspicious user agents
        if (this.isSuspiciousUserAgent(userAgent)) {
            console.warn('Suspicious WebSocket connection attempt:', {
                userAgent,
                origin,
                ip: info.req.socket.remoteAddress
            });
            return false;
        }
        
        return true;
    }
    
    isOriginAllowed(origin) {
        if (this.allowedOrigins.length === 0) {
            return true; // Allow all if none specified (development only)
        }
        
        return this.allowedOrigins.some(allowed => {
            if (typeof allowed === 'string') {
                return origin === allowed;
            }
            if (allowed instanceof RegExp) {
                return allowed.test(origin);
            }
            return false;
        });
    }
    
    isSuspiciousUserAgent(userAgent) {
        if (!userAgent) return true;
        
        const suspiciousPatterns = [
            /bot/i,
            /crawler/i,
            /curl/i,
            /wget/i,
            /python/i
        ];
        
        return suspiciousPatterns.some(pattern => pattern.test(userAgent));
    }
    
    async handleConnection(ws, req) {
        const connectionId = this.generateConnectionId();
        
        try {
            // Extract and verify authentication
            const auth = await this.authenticateConnection(req);
            
            if (!auth.user) {
                ws.close(1008, 'Authentication required');
                return;
            }
            
            // Create secure connection context
            const connection = {
                id: connectionId,
                ws,
                user: auth.user,
                authenticated: true,
                connectedAt: new Date(),
                lastActivity: new Date(),
                subscriptions: new Set(),
                messageCount: 0,
                rateLimit: this.createRateLimit()
            };
            
            this.connections.set(connectionId, connection);
            ws.connectionId = connectionId;
            
            // Send authentication confirmation
            this.sendToConnection(connectionId, {
                type: 'authenticated',
                user: {
                    id: auth.user.id,
                    username: auth.user.username,
                    role: auth.user.role
                },
                connectionId
            });
            
            // Set up message handling
            ws.on('message', (message) => this.handleMessage(connectionId, message));
            ws.on('close', () => this.handleDisconnection(connectionId));
            ws.on('error', (error) => this.handleError(connectionId, error));
            
            console.info('Secure WebSocket connection established:', {
                connectionId,
                userId: auth.user.id,
                origin: req.headers.origin,
                timestamp: new Date().toISOString()
            });
            
        } catch (error) {
            console.error('WebSocket authentication failed:', error);
            ws.close(1008, 'Authentication failed');
        }
    }
    
    async authenticateConnection(req) {
        // Try multiple authentication methods
        
        // Method 1: JWT token in query parameters
        const urlParams = url.parse(req.url, true).query;
        if (urlParams.token) {
            return await this.verifyJWTToken(urlParams.token);
        }
        
        // Method 2: JWT token in Authorization header
        const authHeader = req.headers.authorization;
        if (authHeader && authHeader.startsWith('Bearer ')) {
            const token = authHeader.substring(7);
            return await this.verifyJWTToken(token);
        }
        
        // Method 3: Session-based authentication
        if (req.headers.cookie) {
            return await this.verifySessionCookie(req.headers.cookie);
        }
        
        return { user: null };
    }
    
    async verifyJWTToken(token) {
        try {
            const decoded = jwt.verify(token, this.jwtSecret, {
                algorithms: ['HS256'],
                maxAge: '1h'
            });
            
            // Verify user still exists and is active
            const user = await this.getUserById(decoded.userId);
            if (!user || !user.isActive) {
                throw new Error('User not found or inactive');
            }
            
            return { user, token };
        } catch (error) {
            throw new Error(`JWT verification failed: ${error.message}`);
        }
    }
    
    async verifySessionCookie(cookieHeader) {
        const cookies = this.parseCookies(cookieHeader);
        const sessionId = cookies.sessionId;
        
        if (!sessionId) {
            return { user: null };
        }
        
        // Verify session in secure session store
        const session = await this.getSession(sessionId);
        if (!session || session.expired) {
            return { user: null };
        }
        
        const user = await this.getUserById(session.userId);
        return { user, sessionId };
    }
    
    createRateLimit() {
        return {
            messages: {
                count: 0,
                resetTime: Date.now() + 60000, // 1 minute window
                limit: 60 // 60 messages per minute
            },
            subscriptions: {
                count: 0,
                resetTime: Date.now() + 300000, // 5 minute window
                limit: 10 // 10 subscriptions per 5 minutes
            }
        };
    }
    
    async handleMessage(connectionId, rawMessage) {
        const connection = this.connections.get(connectionId);
        if (!connection) return;
        
        try {
            // Check rate limits
            if (!this.checkRateLimit(connection)) {
                this.sendError(connectionId, 'Rate limit exceeded', 'RATE_LIMIT');
                return;
            }
            
            // Validate message size
            if (rawMessage.length > 10240) { // 10KB limit
                this.sendError(connectionId, 'Message too large', 'MESSAGE_TOO_LARGE');
                return;
            }
            
            const message = JSON.parse(rawMessage);
            
            // Validate message structure
            if (!this.isValidMessage(message)) {
                this.sendError(connectionId, 'Invalid message format', 'INVALID_FORMAT');
                return;
            }
            
            // Update activity timestamp
            connection.lastActivity = new Date();
            connection.messageCount++;
            
            // Route message based on type
            await this.routeMessage(connectionId, message);
            
        } catch (error) {
            console.error('Message handling error:', {
                connectionId,
                error: error.message,
                userId: connection.user.id
            });
            
            this.sendError(connectionId, 'Message processing failed', 'PROCESSING_ERROR');
        }
    }
    
    checkRateLimit(connection) {
        const now = Date.now();
        const messageLimit = connection.rateLimit.messages;
        
        // Reset counter if window expired
        if (now > messageLimit.resetTime) {
            messageLimit.count = 0;
            messageLimit.resetTime = now + 60000;
        }
        
        // Check if limit exceeded
        if (messageLimit.count >= messageLimit.limit) {
            return false;
        }
        
        messageLimit.count++;
        return true;
    }
    
    async routeMessage(connectionId, message) {
        const connection = this.connections.get(connectionId);
        
        switch (message.type) {
            case 'subscribe':
                await this.handleSubscription(connectionId, message);
                break;
            case 'unsubscribe':
                await this.handleUnsubscription(connectionId, message);
                break;
            case 'chat_message':
                await this.handleChatMessage(connectionId, message);
                break;
            case 'admin_action':
                await this.handleAdminAction(connectionId, message);
                break;
            default:
                this.sendError(connectionId, 'Unknown message type', 'UNKNOWN_TYPE');
        }
    }
    
    async handleSubscription(connectionId, message) {
        const connection = this.connections.get(connectionId);
        const channel = message.channel;
        
        // Validate channel access
        if (!await this.canAccessChannel(connection.user, channel)) {
            this.sendError(connectionId, 'Access denied to channel', 'ACCESS_DENIED');
            return;
        }
        
        // Check subscription rate limit
        const subLimit = connection.rateLimit.subscriptions;
        if (Date.now() <= subLimit.resetTime && subLimit.count >= subLimit.limit) {
            this.sendError(connectionId, 'Subscription rate limit exceeded', 'SUB_RATE_LIMIT');
            return;
        }
        
        // Add to channel
        if (!this.channels.has(channel)) {
            this.channels.set(channel, new Set());
        }
        
        this.channels.get(channel).add(connectionId);
        connection.subscriptions.add(channel);
        subLimit.count++;
        
        this.sendToConnection(connectionId, {
            type: 'subscribed',
            channel,
            timestamp: new Date().toISOString()
        });
    }
    
    async canAccessChannel(user, channel) {
        // Implement channel-specific authorization logic
        const channelPermissions = {
            'public.*': () => true,
            'user.*': () => user.role === 'user' || user.role === 'admin',
            'admin.*': () => user.role === 'admin',
            'private.*': () => this.hasPrivateChannelAccess(user, channel)
        };
        
        for (const [pattern, checkFn] of Object.entries(channelPermissions)) {
            if (this.matchesPattern(channel, pattern)) {
                return checkFn();
            }
        }
        
        return false;
    }
    
    generateConnectionId() {
        return crypto.randomBytes(16).toString('hex');
    }
    
    sendToConnection(connectionId, message) {
        const connection = this.connections.get(connectionId);
        if (connection && connection.ws.readyState === WebSocket.OPEN) {
            connection.ws.send(JSON.stringify(message));
        }
    }
    
    sendError(connectionId, message, code) {
        this.sendToConnection(connectionId, {
            type: 'error',
            error: message,
            code,
            timestamp: new Date().toISOString()
        });
    }
}
2

Implement Origin Validation and CSRF Protection

Add comprehensive origin validation, CSRF token verification, and secure connection establishment to prevent cross-site WebSocket hijacking attacks.

View implementation – JAVASCRIPT
const WebSocket = require('ws');
const crypto = require('crypto');
const url = require('url');

// Secure WebSocket server with CSRF protection
class CSRFProtectedWebSocketServer {
    constructor(options) {
        this.allowedOrigins = options.allowedOrigins || [];
        this.csrfTokens = new Map(); // In production, use Redis
        this.strictOriginCheck = options.strictOriginCheck !== false;
        
        this.wss = new WebSocket.Server({
            ...options,
            verifyClient: this.verifyClient.bind(this)
        });
        
        this.wss.on('connection', this.handleConnection.bind(this));
    }
    
    verifyClient(info) {
        const { origin, req } = info;
        const userAgent = req.headers['user-agent'];
        const ip = req.socket.remoteAddress;
        
        // Step 1: Validate origin header
        if (this.strictOriginCheck) {
            if (!origin) {
                this.logSecurityViolation('Missing origin header', {
                    ip, userAgent, type: 'MISSING_ORIGIN'
                });
                return false;
            }
            
            if (!this.isOriginAllowed(origin)) {
                this.logSecurityViolation('Unauthorized origin', {
                    origin, ip, userAgent, type: 'INVALID_ORIGIN'
                });
                return false;
            }
        }
        
        // Step 2: Validate CSRF token
        const urlParams = url.parse(req.url, true).query;
        const csrfToken = urlParams.csrf_token || req.headers['x-csrf-token'];
        
        if (!this.validateCSRFToken(csrfToken, req)) {
            this.logSecurityViolation('Invalid CSRF token', {
                origin, ip, userAgent, type: 'INVALID_CSRF'
            });
            return false;
        }
        
        // Step 3: Additional security checks
        if (!this.passesSecurityChecks(req)) {
            return false;
        }
        
        return true;
    }
    
    isOriginAllowed(origin) {
        try {
            const url = new URL(origin);
            
            // Check protocol (only HTTPS in production)
            if (process.env.NODE_ENV === 'production' && url.protocol !== 'https:') {
                return false;
            }
            
            // Check against whitelist
            return this.allowedOrigins.some(allowed => {
                if (typeof allowed === 'string') {
                    return origin === allowed;
                }
                if (allowed instanceof RegExp) {
                    return allowed.test(origin);
                }
                if (typeof allowed === 'object' && allowed.domain) {
                    return url.hostname === allowed.domain ||
                           (allowed.includeSubdomains && url.hostname.endsWith(`.${allowed.domain}`));
                }
                return false;
            });
        } catch (error) {
            return false;
        }
    }
    
    validateCSRFToken(token, req) {
        if (!token) {
            return false;
        }
        
        // Verify token format
        if (!/^[a-f0-9]{64}$/.test(token)) {
            return false;
        }
        
        // Check if token exists and is valid
        const tokenData = this.csrfTokens.get(token);
        if (!tokenData) {
            return false;
        }
        
        // Check expiration
        if (Date.now() > tokenData.expiresAt) {
            this.csrfTokens.delete(token);
            return false;
        }
        
        // Verify origin matches
        const origin = req.headers.origin;
        if (tokenData.origin && tokenData.origin !== origin) {
            return false;
        }
        
        // Verify user session (if available)
        if (tokenData.sessionId) {
            const cookies = this.parseCookies(req.headers.cookie || '');
            if (cookies.sessionId !== tokenData.sessionId) {
                return false;
            }
        }
        
        return true;
    }
    
    passesSecurityChecks(req) {
        const userAgent = req.headers['user-agent'];
        const ip = req.socket.remoteAddress;
        
        // Check for suspicious user agents
        const suspiciousUAs = [
            /bot/i, /crawler/i, /spider/i, /scraper/i,
            /curl/i, /wget/i, /python/i, /java/i
        ];
        
        if (suspiciousUAs.some(pattern => pattern.test(userAgent))) {
            this.logSecurityViolation('Suspicious user agent', {
                userAgent, ip, type: 'SUSPICIOUS_UA'
            });
            return false;
        }
        
        // Check rate limiting per IP
        if (!this.checkIPRateLimit(ip)) {
            this.logSecurityViolation('IP rate limit exceeded', {
                ip, type: 'IP_RATE_LIMIT'
            });
            return false;
        }
        
        return true;
    }
    
    checkIPRateLimit(ip) {
        const key = `ip_limit:${ip}`;
        const now = Date.now();
        const window = 60000; // 1 minute
        const limit = 10; // 10 connections per minute per IP
        
        if (!this.ipLimits) {
            this.ipLimits = new Map();
        }
        
        const current = this.ipLimits.get(key) || { count: 0, resetTime: now + window };
        
        // Reset if window expired
        if (now > current.resetTime) {
            current.count = 0;
            current.resetTime = now + window;
        }
        
        // Check limit
        if (current.count >= limit) {
            return false;
        }
        
        current.count++;
        this.ipLimits.set(key, current);
        
        return true;
    }
    
    logSecurityViolation(message, details) {
        console.warn('WebSocket security violation:', {
            message,
            ...details,
            timestamp: new Date().toISOString()
        });
        
        // In production, send to security monitoring system
        // this.sendSecurityAlert(message, details);
    }
    
    // Generate CSRF token for client use
    generateCSRFToken(origin, sessionId = null) {
        const token = crypto.randomBytes(32).toString('hex');
        const expiresAt = Date.now() + (15 * 60 * 1000); // 15 minutes
        
        this.csrfTokens.set(token, {
            origin,
            sessionId,
            expiresAt,
            createdAt: Date.now()
        });
        
        // Clean up expired tokens periodically
        this.cleanupExpiredTokens();
        
        return { token, expiresAt };
    }
    
    cleanupExpiredTokens() {
        const now = Date.now();
        let cleaned = 0;
        
        for (const [token, data] of this.csrfTokens.entries()) {
            if (now > data.expiresAt) {
                this.csrfTokens.delete(token);
                cleaned++;
            }
        }
        
        if (cleaned > 0) {
            console.debug(`Cleaned up ${cleaned} expired CSRF tokens`);
        }
    }
    
    parseCookies(cookieHeader) {
        const cookies = {};
        cookieHeader.split(';').forEach(cookie => {
            const [name, value] = cookie.trim().split('=');
            if (name && value) {
                cookies[name] = decodeURIComponent(value);
            }
        });
        return cookies;
    }
    
    handleConnection(ws, req) {
        const origin = req.headers.origin;
        const ip = req.socket.remoteAddress;
        
        console.info('Secure WebSocket connection established:', {
            origin,
            ip: ip?.replace(/\d+/g, 'XXX'), // Anonymize for logging
            timestamp: new Date().toISOString()
        });
        
        // Set connection security headers
        ws.securityContext = {
            origin,
            establishedAt: new Date(),
            lastActivity: new Date(),
            messageCount: 0
        };
        
        ws.on('message', (message) => {
            this.handleSecureMessage(ws, message);
        });
        
        ws.on('close', () => {
            console.debug('Secure WebSocket connection closed');
        });
        
        // Send security confirmation
        ws.send(JSON.stringify({
            type: 'security_established',
            origin,
            timestamp: new Date().toISOString()
        }));
    }
    
    handleSecureMessage(ws, rawMessage) {
        try {
            // Validate message origin consistency
            const origin = ws.securityContext.origin;
            
            // Update activity tracking
            ws.securityContext.lastActivity = new Date();
            ws.securityContext.messageCount++;
            
            // Check for suspicious activity
            if (this.detectSuspiciousActivity(ws)) {
                ws.close(1008, 'Suspicious activity detected');
                return;
            }
            
            const message = JSON.parse(rawMessage);
            
            // Additional message validation
            if (!this.validateMessageSecurity(message, ws)) {
                ws.close(1008, 'Security validation failed');
                return;
            }
            
            // Process message...
            this.processSecureMessage(ws, message);
            
        } catch (error) {
            console.error('Secure message handling error:', error);
            ws.close(1008, 'Message processing error');
        }
    }
    
    detectSuspiciousActivity(ws) {
        const context = ws.securityContext;
        const now = Date.now();
        const timeSinceEstablished = now - context.establishedAt.getTime();
        
        // Check for rapid message sending
        if (context.messageCount > 100 && timeSinceEstablished < 60000) {
            return true; // More than 100 messages in first minute
        }
        
        // Check for long periods of inactivity followed by bursts
        const timeSinceLastActivity = now - context.lastActivity.getTime();
        if (timeSinceLastActivity > 300000 && context.messageCount > 50) {
            return true; // Inactive for 5 minutes then burst of messages
        }
        
        return false;
    }
    
    validateMessageSecurity(message, ws) {
        // Validate message structure
        if (typeof message !== 'object' || !message.type) {
            return false;
        }
        
        // Check for injection attempts
        const dangerousPatterns = [
            /<script/i,
            /javascript:/i,
            /on\w+=/i,
            /eval\(/i,
            /function\s*\(/i
        ];
        
        const messageStr = JSON.stringify(message);
        if (dangerousPatterns.some(pattern => pattern.test(messageStr))) {
            this.logSecurityViolation('Potential injection attempt', {
                origin: ws.securityContext.origin,
                messageType: message.type,
                type: 'INJECTION_ATTEMPT'
            });
            return false;
        }
        
        return true;
    }
}

// Express endpoint to generate CSRF tokens
app.get('/api/websocket/csrf-token', (req, res) => {
    const origin = req.headers.origin;
    const sessionId = req.session?.id;
    
    if (!wsServer.isOriginAllowed(origin)) {
        return res.status(403).json({ error: 'Origin not allowed' });
    }
    
    const csrfData = wsServer.generateCSRFToken(origin, sessionId);
    
    res.json({
        csrf_token: csrfData.token,
        expires_at: csrfData.expiresAt,
        websocket_url: `wss://your-domain.com/ws?csrf_token=${csrfData.token}`
    });
});
3

Implement Message Validation and Input Sanitization

Add comprehensive message validation, input sanitization, and content filtering to prevent message injection attacks and ensure data integrity in WebSocket communications.

View implementation – JAVASCRIPT
const WebSocket = require('ws');
const DOMPurify = require('isomorphic-dompurify');
const validator = require('validator');
const rateLimit = require('express-rate-limit');

// Secure message validation and sanitization
class SecureMessageHandler {
    constructor(options = {}) {
        this.maxMessageSize = options.maxMessageSize || 10240; // 10KB
        this.maxBatchSize = options.maxBatchSize || 10;
        this.allowedMessageTypes = options.allowedMessageTypes || [
            'chat', 'subscribe', 'unsubscribe', 'ping', 'user_action'
        ];
        
        // Message schemas for validation
        this.messageSchemas = {
            chat: {
                required: ['type', 'message', 'channel'],
                optional: ['replyTo', 'mentions'],
                validation: {
                    message: { type: 'string', maxLength: 1000, sanitize: true },
                    channel: { type: 'string', pattern: /^[a-zA-Z0-9_-]+$/, maxLength: 50 },
                    replyTo: { type: 'string', pattern: /^[a-f0-9]{24}$/ },
                    mentions: { type: 'array', maxItems: 10, itemType: 'string' }
                }
            },
            subscribe: {
                required: ['type', 'channel'],
                validation: {
                    channel: { type: 'string', pattern: /^[a-zA-Z0-9_.-]+$/, maxLength: 100 }
                }
            },
            user_action: {
                required: ['type', 'action'],
                optional: ['data'],
                validation: {
                    action: { 
                        type: 'string', 
                        enum: ['like', 'follow', 'block', 'report'],
                        maxLength: 20
                    },
                    data: { type: 'object', maxProperties: 5 }
                }
            }
        };
    }
    
    validateAndSanitizeMessage(rawMessage, userContext) {
        // Step 1: Size validation
        if (rawMessage.length > this.maxMessageSize) {
            throw new ValidationError('Message too large', 'MESSAGE_TOO_LARGE');
        }
        
        // Step 2: JSON parsing with error handling
        let message;
        try {
            message = JSON.parse(rawMessage);
        } catch (error) {
            throw new ValidationError('Invalid JSON format', 'INVALID_JSON');
        }
        
        // Step 3: Basic structure validation
        if (!message || typeof message !== 'object') {
            throw new ValidationError('Message must be an object', 'INVALID_STRUCTURE');
        }
        
        if (!message.type || typeof message.type !== 'string') {
            throw new ValidationError('Message type required', 'MISSING_TYPE');
        }
        
        // Step 4: Message type validation
        if (!this.allowedMessageTypes.includes(message.type)) {
            throw new ValidationError('Invalid message type', 'INVALID_TYPE');
        }
        
        // Step 5: Schema-based validation
        const schema = this.messageSchemas[message.type];
        if (schema) {
            message = this.validateSchema(message, schema);
        }
        
        // Step 6: Security validation
        this.validateSecurity(message);
        
        // Step 7: User context validation
        this.validateUserContext(message, userContext);
        
        // Step 8: Content sanitization
        return this.sanitizeContent(message);
    }
    
    validateSchema(message, schema) {
        const validated = { type: message.type };
        
        // Check required fields
        for (const field of schema.required) {
            if (!(field in message)) {
                throw new ValidationError(`Required field missing: ${field}`, 'MISSING_REQUIRED');
            }
        }
        
        // Validate all present fields
        for (const [field, value] of Object.entries(message)) {
            if (field === 'type') continue; // Already validated
            
            const fieldSchema = schema.validation?.[field];
            if (!fieldSchema) {
                // Check if field is allowed
                if (!schema.optional?.includes(field)) {
                    throw new ValidationError(`Unknown field: ${field}`, 'UNKNOWN_FIELD');
                }
                validated[field] = value;
                continue;
            }
            
            validated[field] = this.validateField(field, value, fieldSchema);
        }
        
        return validated;
    }
    
    validateField(fieldName, value, schema) {
        // Type validation
        switch (schema.type) {
            case 'string':
                if (typeof value !== 'string') {
                    throw new ValidationError(`${fieldName} must be a string`, 'INVALID_TYPE');
                }
                
                // Length validation
                if (schema.maxLength && value.length > schema.maxLength) {
                    throw new ValidationError(`${fieldName} too long`, 'VALUE_TOO_LONG');
                }
                
                if (schema.minLength && value.length < schema.minLength) {
                    throw new ValidationError(`${fieldName} too short`, 'VALUE_TOO_SHORT');
                }
                
                // Pattern validation
                if (schema.pattern && !schema.pattern.test(value)) {
                    throw new ValidationError(`${fieldName} format invalid`, 'INVALID_FORMAT');
                }
                
                // Enum validation
                if (schema.enum && !schema.enum.includes(value)) {
                    throw new ValidationError(`${fieldName} invalid value`, 'INVALID_ENUM');
                }
                
                // Sanitization
                if (schema.sanitize) {
                    value = this.sanitizeString(value);
                }
                
                break;
                
            case 'number':
                if (typeof value !== 'number' || isNaN(value)) {
                    throw new ValidationError(`${fieldName} must be a number`, 'INVALID_TYPE');
                }
                
                if (schema.min !== undefined && value < schema.min) {
                    throw new ValidationError(`${fieldName} below minimum`, 'VALUE_TOO_LOW');
                }
                
                if (schema.max !== undefined && value > schema.max) {
                    throw new ValidationError(`${fieldName} above maximum`, 'VALUE_TOO_HIGH');
                }
                
                break;
                
            case 'array':
                if (!Array.isArray(value)) {
                    throw new ValidationError(`${fieldName} must be an array`, 'INVALID_TYPE');
                }
                
                if (schema.maxItems && value.length > schema.maxItems) {
                    throw new ValidationError(`${fieldName} too many items`, 'TOO_MANY_ITEMS');
                }
                
                // Validate array items
                if (schema.itemType) {
                    value = value.map((item, index) => {
                        if (schema.itemType === 'string' && typeof item !== 'string') {
                            throw new ValidationError(`${fieldName}[${index}] must be string`, 'INVALID_ITEM_TYPE');
                        }
                        
                        if (schema.itemType === 'string' && schema.sanitizeItems) {
                            return this.sanitizeString(item);
                        }
                        
                        return item;
                    });
                }
                
                break;
                
            case 'object':
                if (typeof value !== 'object' || value === null || Array.isArray(value)) {
                    throw new ValidationError(`${fieldName} must be an object`, 'INVALID_TYPE');
                }
                
                if (schema.maxProperties && Object.keys(value).length > schema.maxProperties) {
                    throw new ValidationError(`${fieldName} too many properties`, 'TOO_MANY_PROPS');
                }
                
                break;
        }
        
        return value;
    }
    
    validateSecurity(message) {
        const messageStr = JSON.stringify(message);
        
        // Check for potential XSS/injection patterns
        const dangerousPatterns = [
            /<script[^>]*>/i,
            /<\/script>/i,
            /javascript:/i,
            /vbscript:/i,
            /on\w+\s*=/i,
            /eval\s*\(/i,
            /expression\s*\(/i,
            /url\s*\(/i,
            /@import/i,
            /\bdata:(?!image\/[a-z]+;base64,)[^;,]+/i
        ];
        
        for (const pattern of dangerousPatterns) {
            if (pattern.test(messageStr)) {
                throw new SecurityError('Potentially dangerous content detected', 'SECURITY_VIOLATION');
            }
        }
        
        // Check for SQL injection patterns (if message contains query-like content)
        const sqlPatterns = [
            /('|(\-\-)|(;)|(\||\|)|(\*|\*))/i,
            /(union|select|insert|delete|update|drop|create|alter|exec|execute)/i
        ];
        
        for (const pattern of sqlPatterns) {
            if (pattern.test(messageStr)) {
                throw new SecurityError('Potential SQL injection detected', 'SQL_INJECTION');
            }
        }
        
        // Check for command injection
        const commandPatterns = [
            /[;&|`$()]/,
            /(rm|ls|cat|grep|awk|sed|curl|wget|nc|netcat)/i
        ];
        
        for (const pattern of commandPatterns) {
            if (pattern.test(messageStr)) {
                throw new SecurityError('Potential command injection detected', 'COMMAND_INJECTION');
            }
        }
    }
    
    validateUserContext(message, userContext) {
        // Validate user permissions for message type
        const requiredPermissions = {
            'admin_command': ['admin'],
            'moderate_chat': ['admin', 'moderator'],
            'private_message': ['verified']
        };
        
        const required = requiredPermissions[message.type];
        if (required && !required.some(role => userContext.roles?.includes(role))) {
            throw new AuthorizationError('Insufficient permissions', 'INSUFFICIENT_PERMISSIONS');
        }
        
        // Validate channel access
        if (message.channel) {
            if (!this.canAccessChannel(userContext, message.channel)) {
                throw new AuthorizationError('Channel access denied', 'CHANNEL_ACCESS_DENIED');
            }
        }
        
        // Validate rate limits
        if (!this.checkUserRateLimit(userContext, message.type)) {
            throw new RateLimitError('User rate limit exceeded', 'USER_RATE_LIMIT');
        }
    }
    
    sanitizeContent(message) {
        const sanitized = { ...message };
        
        // Sanitize string fields
        for (const [key, value] of Object.entries(sanitized)) {
            if (typeof value === 'string') {
                sanitized[key] = this.sanitizeString(value);
            } else if (Array.isArray(value)) {
                sanitized[key] = value.map(item => 
                    typeof item === 'string' ? this.sanitizeString(item) : item
                );
            }
        }
        
        return sanitized;
    }
    
    sanitizeString(input) {
        if (!input || typeof input !== 'string') {
            return input;
        }
        
        // Remove null bytes
        let sanitized = input.replace(/\x00/g, '');
        
        // HTML sanitization using DOMPurify
        sanitized = DOMPurify.sanitize(sanitized, {
            ALLOWED_TAGS: [], // No HTML tags allowed
            ALLOWED_ATTR: [],
            KEEP_CONTENT: true
        });
        
        // Additional sanitization
        sanitized = sanitized
            .replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g, '') // Control characters
            .trim();
        
        // Validate as safe UTF-8
        if (!validator.isAscii(sanitized) && !this.isValidUTF8(sanitized)) {
            throw new ValidationError('Invalid character encoding', 'INVALID_ENCODING');
        }
        
        return sanitized;
    }
    
    isValidUTF8(str) {
        try {
            return str === decodeURIComponent(encodeURIComponent(str));
        } catch (e) {
            return false;
        }
    }
    
    canAccessChannel(userContext, channel) {
        // Implement channel access logic
        const publicChannels = /^public\./;
        const userChannels = /^user\./;
        const adminChannels = /^admin\./;
        
        if (publicChannels.test(channel)) {
            return true;
        }
        
        if (userChannels.test(channel)) {
            return userContext.authenticated;
        }
        
        if (adminChannels.test(channel)) {
            return userContext.roles?.includes('admin');
        }
        
        return false;
    }
    
    checkUserRateLimit(userContext, messageType) {
        // Implement per-user rate limiting
        const limits = {
            'chat': { window: 60000, max: 30 }, // 30 messages per minute
            'subscribe': { window: 300000, max: 10 }, // 10 subscriptions per 5 minutes
            'user_action': { window: 60000, max: 20 } // 20 actions per minute
        };
        
        const limit = limits[messageType];
        if (!limit) return true;
        
        // Implementation would use Redis or similar for distributed rate limiting
        // This is a simplified version
        return true;
    }
}

// Custom error classes
class ValidationError extends Error {
    constructor(message, code) {
        super(message);
        this.name = 'ValidationError';
        this.code = code;
    }
}

class SecurityError extends Error {
    constructor(message, code) {
        super(message);
        this.name = 'SecurityError';
        this.code = code;
    }
}

class AuthorizationError extends Error {
    constructor(message, code) {
        super(message);
        this.name = 'AuthorizationError';
        this.code = code;
    }
}

class RateLimitError extends Error {
    constructor(message, code) {
        super(message);
        this.name = 'RateLimitError';
        this.code = code;
    }
}
4

Implement WebSocket Rate Limiting and Resource Management

Add comprehensive rate limiting, connection management, and resource controls to prevent denial of service attacks and ensure fair usage of WebSocket resources.

View implementation – JAVASCRIPT
const WebSocket = require('ws');
const EventEmitter = require('events');

// Comprehensive WebSocket rate limiting and resource management
class WebSocketResourceManager extends EventEmitter {
    constructor(options = {}) {
        super();
        
        this.config = {
            maxConnections: options.maxConnections || 1000,
            maxConnectionsPerIP: options.maxConnectionsPerIP || 10,
            maxMessageRate: options.maxMessageRate || 60, // messages per minute
            maxMessageSize: options.maxMessageSize || 10240, // 10KB
            maxSubscriptionsPerConnection: options.maxSubscriptionsPerConnection || 20,
            connectionTimeoutMs: options.connectionTimeoutMs || 300000, // 5 minutes
            messageQueueSize: options.messageQueueSize || 100,
            cleanupIntervalMs: options.cleanupIntervalMs || 60000 // 1 minute
        };
        
        this.connections = new Map();
        this.ipConnections = new Map();
        this.rateLimits = new Map();
        this.stats = {
            totalConnections: 0,
            activeConnections: 0,
            messagesProcessed: 0,
            messagesDropped: 0,
            connectionsRejected: 0
        };
        
        // Start cleanup interval
        this.cleanupInterval = setInterval(() => {
            this.cleanup();
        }, this.config.cleanupIntervalMs);
    }
    
    canAcceptConnection(req) {
        const ip = this.getClientIP(req);
        
        // Check global connection limit
        if (this.stats.activeConnections >= this.config.maxConnections) {
            this.stats.connectionsRejected++;
            this.emit('limitExceeded', {
                type: 'GLOBAL_CONNECTION_LIMIT',
                ip,
                currentCount: this.stats.activeConnections,
                limit: this.config.maxConnections
            });
            return false;
        }
        
        // Check per-IP connection limit
        const ipConnectionCount = this.ipConnections.get(ip)?.size || 0;
        if (ipConnectionCount >= this.config.maxConnectionsPerIP) {
            this.stats.connectionsRejected++;
            this.emit('limitExceeded', {
                type: 'IP_CONNECTION_LIMIT',
                ip,
                currentCount: ipConnectionCount,
                limit: this.config.maxConnectionsPerIP
            });
            return false;
        }
        
        return true;
    }
    
    registerConnection(ws, req) {
        const connectionId = this.generateConnectionId();
        const ip = this.getClientIP(req);
        const now = Date.now();
        
        const connection = {
            id: connectionId,
            ws,
            ip,
            connectedAt: now,
            lastActivity: now,
            messageCount: 0,
            subscriptions: new Set(),
            messageQueue: [],
            rateLimitResets: {
                messages: now + 60000, // 1 minute window
                subscriptions: now + 300000 // 5 minute window
            },
            limits: {
                messagesThisWindow: 0,
                subscriptionsThisWindow: 0
            }
        };
        
        // Register connection
        this.connections.set(connectionId, connection);
        
        // Track IP connections
        if (!this.ipConnections.has(ip)) {
            this.ipConnections.set(ip, new Set());
        }
        this.ipConnections.get(ip).add(connectionId);
        
        // Update stats
        this.stats.totalConnections++;
        this.stats.activeConnections++;
        
        // Set up connection handlers
        ws.connectionId = connectionId;
        ws.on('close', () => this.unregisterConnection(connectionId));
        ws.on('error', () => this.unregisterConnection(connectionId));
        
        this.emit('connectionRegistered', {
            connectionId,
            ip,
            totalConnections: this.stats.activeConnections
        });
        
        return connectionId;
    }
    
    unregisterConnection(connectionId) {
        const connection = this.connections.get(connectionId);
        if (!connection) return;
        
        const ip = connection.ip;
        
        // Remove from connections
        this.connections.delete(connectionId);
        
        // Remove from IP tracking
        const ipSet = this.ipConnections.get(ip);
        if (ipSet) {
            ipSet.delete(connectionId);
            if (ipSet.size === 0) {
                this.ipConnections.delete(ip);
            }
        }
        
        // Update stats
        this.stats.activeConnections--;
        
        this.emit('connectionUnregistered', {
            connectionId,
            ip,
            duration: Date.now() - connection.connectedAt,
            messagesProcessed: connection.messageCount
        });
    }
    
    canProcessMessage(connectionId, messageSize) {
        const connection = this.connections.get(connectionId);
        if (!connection) return false;
        
        const now = Date.now();
        
        // Check message size limit
        if (messageSize > this.config.maxMessageSize) {
            this.emit('limitExceeded', {
                type: 'MESSAGE_SIZE_LIMIT',
                connectionId,
                messageSize,
                limit: this.config.maxMessageSize
            });
            return false;
        }
        
        // Reset rate limit window if expired
        if (now > connection.rateLimitResets.messages) {
            connection.limits.messagesThisWindow = 0;
            connection.rateLimitResets.messages = now + 60000;
        }
        
        // Check message rate limit
        if (connection.limits.messagesThisWindow >= this.config.maxMessageRate) {
            this.stats.messagesDropped++;
            this.emit('limitExceeded', {
                type: 'MESSAGE_RATE_LIMIT',
                connectionId,
                currentRate: connection.limits.messagesThisWindow,
                limit: this.config.maxMessageRate
            });
            return false;
        }
        
        // Check message queue size
        if (connection.messageQueue.length >= this.config.messageQueueSize) {
            this.stats.messagesDropped++;
            this.emit('limitExceeded', {
                type: 'MESSAGE_QUEUE_FULL',
                connectionId,
                queueSize: connection.messageQueue.length,
                limit: this.config.messageQueueSize
            });
            return false;
        }
        
        return true;
    }
    
    recordMessage(connectionId, messageSize) {
        const connection = this.connections.get(connectionId);
        if (!connection) return;
        
        connection.lastActivity = Date.now();
        connection.messageCount++;
        connection.limits.messagesThisWindow++;
        this.stats.messagesProcessed++;
        
        // Add to processing queue
        connection.messageQueue.push({
            timestamp: Date.now(),
            size: messageSize
        });
    }
    
    canSubscribe(connectionId, channel) {
        const connection = this.connections.get(connectionId);
        if (!connection) return false;
        
        const now = Date.now();
        
        // Check subscription limit
        if (connection.subscriptions.size >= this.config.maxSubscriptionsPerConnection) {
            this.emit('limitExceeded', {
                type: 'SUBSCRIPTION_LIMIT',
                connectionId,
                currentCount: connection.subscriptions.size,
                limit: this.config.maxSubscriptionsPerConnection
            });
            return false;
        }
        
        // Reset subscription rate limit window if expired
        if (now > connection.rateLimitResets.subscriptions) {
            connection.limits.subscriptionsThisWindow = 0;
            connection.rateLimitResets.subscriptions = now + 300000;
        }
        
        // Check subscription rate limit (prevent subscription flooding)
        if (connection.limits.subscriptionsThisWindow >= 10) {
            this.emit('limitExceeded', {
                type: 'SUBSCRIPTION_RATE_LIMIT',
                connectionId,
                currentRate: connection.limits.subscriptionsThisWindow,
                limit: 10
            });
            return false;
        }
        
        return true;
    }
    
    recordSubscription(connectionId, channel) {
        const connection = this.connections.get(connectionId);
        if (!connection) return;
        
        connection.subscriptions.add(channel);
        connection.limits.subscriptionsThisWindow++;
        
        this.emit('subscriptionAdded', {
            connectionId,
            channel,
            totalSubscriptions: connection.subscriptions.size
        });
    }
    
    removeSubscription(connectionId, channel) {
        const connection = this.connections.get(connectionId);
        if (!connection) return;
        
        connection.subscriptions.delete(channel);
        
        this.emit('subscriptionRemoved', {
            connectionId,
            channel,
            totalSubscriptions: connection.subscriptions.size
        });
    }
    
    cleanup() {
        const now = Date.now();
        let cleanedConnections = 0;
        let cleanedMessages = 0;
        
        // Clean up inactive connections
        for (const [connectionId, connection] of this.connections.entries()) {
            const inactiveTime = now - connection.lastActivity;
            
            // Close connections that have been inactive too long
            if (inactiveTime > this.config.connectionTimeoutMs) {
                try {
                    connection.ws.close(1000, 'Inactive connection timeout');
                } catch (error) {
                    // Connection already closed
                }
                this.unregisterConnection(connectionId);
                cleanedConnections++;
                continue;
            }
            
            // Clean up old messages from queue
            const oldMessageCount = connection.messageQueue.length;
            connection.messageQueue = connection.messageQueue.filter(
                msg => now - msg.timestamp < 300000 // Keep messages for 5 minutes
            );
            cleanedMessages += oldMessageCount - connection.messageQueue.length;
        }
        
        if (cleanedConnections > 0 || cleanedMessages > 0) {
            this.emit('cleanup', {
                cleanedConnections,
                cleanedMessages,
                activeConnections: this.stats.activeConnections
            });
        }
    }
    
    getConnectionStats(connectionId) {
        const connection = this.connections.get(connectionId);
        if (!connection) return null;
        
        const now = Date.now();
        return {
            id: connectionId,
            ip: connection.ip?.replace(/\d+/g, 'XXX'), // Anonymize for privacy
            connectedAt: connection.connectedAt,
            lastActivity: connection.lastActivity,
            uptime: now - connection.connectedAt,
            inactiveTime: now - connection.lastActivity,
            messageCount: connection.messageCount,
            subscriptionCount: connection.subscriptions.size,
            queueSize: connection.messageQueue.length,
            limits: {
                messagesThisWindow: connection.limits.messagesThisWindow,
                subscriptionsThisWindow: connection.limits.subscriptionsThisWindow
            }
        };
    }
    
    getGlobalStats() {
        return {
            ...this.stats,
            connectionsPerIP: Array.from(this.ipConnections.entries()).map(
                ([ip, connections]) => ({
                    ip: ip.replace(/\d+/g, 'XXX'),
                    count: connections.size
                })
            ).filter(entry => entry.count > 1), // Only show IPs with multiple connections
            avgConnectionDuration: this.calculateAvgConnectionDuration(),
            topChannels: this.getTopChannels()
        };
    }
    
    calculateAvgConnectionDuration() {
        if (this.connections.size === 0) return 0;
        
        const now = Date.now();
        const totalDuration = Array.from(this.connections.values())
            .reduce((sum, conn) => sum + (now - conn.connectedAt), 0);
        
        return Math.round(totalDuration / this.connections.size);
    }
    
    getTopChannels() {
        const channelCounts = new Map();
        
        for (const connection of this.connections.values()) {
            for (const channel of connection.subscriptions) {
                channelCounts.set(channel, (channelCounts.get(channel) || 0) + 1);
            }
        }
        
        return Array.from(channelCounts.entries())
            .sort((a, b) => b[1] - a[1])
            .slice(0, 10)
            .map(([channel, count]) => ({ channel, subscribers: count }));
    }
    
    getClientIP(req) {
        return req.headers['x-forwarded-for']?.split(',')[0]?.trim() ||
               req.headers['x-real-ip'] ||
               req.socket.remoteAddress ||
               'unknown';
    }
    
    generateConnectionId() {
        return require('crypto').randomBytes(16).toString('hex');
    }
    
    destroy() {
        if (this.cleanupInterval) {
            clearInterval(this.cleanupInterval);
        }
        
        // Close all connections
        for (const connection of this.connections.values()) {
            try {
                connection.ws.close(1001, 'Server shutting down');
            } catch (error) {
                // Ignore errors during shutdown
            }
        }
        
        this.connections.clear();
        this.ipConnections.clear();
        this.rateLimits.clear();
    }
}

// Usage example with WebSocket server
const resourceManager = new WebSocketResourceManager({
    maxConnections: 1000,
    maxConnectionsPerIP: 5,
    maxMessageRate: 30, // 30 messages per minute
    maxMessageSize: 8192, // 8KB
    maxSubscriptionsPerConnection: 15
});

const wss = new WebSocket.Server({
    port: 8080,
    verifyClient: (info) => {
        return resourceManager.canAcceptConnection(info.req);
    }
});

wss.on('connection', (ws, req) => {
    const connectionId = resourceManager.registerConnection(ws, req);
    
    ws.on('message', (rawMessage) => {
        const messageSize = Buffer.byteLength(rawMessage);
        
        if (!resourceManager.canProcessMessage(connectionId, messageSize)) {
            ws.send(JSON.stringify({
                type: 'error',
                code: 'RATE_LIMITED',
                message: 'Rate limit exceeded'
            }));
            return;
        }
        
        resourceManager.recordMessage(connectionId, messageSize);
        
        // Process message...
        try {
            const message = JSON.parse(rawMessage);
            // Handle message based on type
        } catch (error) {
            ws.send(JSON.stringify({
                type: 'error',
                code: 'INVALID_JSON',
                message: 'Invalid message format'
            }));
        }
    });
});

// Monitor resource usage
resourceManager.on('limitExceeded', (event) => {
    console.warn('Resource limit exceeded:', event);
});

resourceManager.on('cleanup', (event) => {
    console.info('Resource cleanup completed:', event);
});

Detect This Vulnerability in Your Code

Sourcery automatically identifies websocket api security vulnerabilities and authentication bypass and many other security issues in your codebase.