WebSocket 连接有哪些安全风险?如何防护?
WebSocket 的安全威胁主要来自四方面:窃听、劫持、伪造和洪泛。防护的核心是强制 WSS(TLS 加密)防止窃听,验证 Origin 头防止跨站 WebSocket 劫持(CSWSH),在握手阶段或首条消息中完成身份认证防止伪造,速率限制和消息校验防止洪泛和注入攻击。WSS 是底线——ws:// 明文传输下,中间人可以读取甚至篡改所有消息。Origin 验证同样关键:浏览器发起 WebSocket 连接时会自动携带 Cookie,如果服务端不校验 Origin,恶意页面可以伪造用户身份建立连接(即 CSWSH 攻击)。认证方面,URL 查询参数传 token 会泄露到日志,更安全的做法是连接建立后通过首条消息发送 JWT,服务端验证失败立即关闭连接(code=1008)。
追问
CSWSH 攻击是怎么发生的?和 CSRF 有什么区别?
CSWSH 利用浏览器自动携带 Cookie 的特性:恶意网站用 JS 创建 new WebSocket('wss://victim.com/socket'),浏览器会带上 victim.com 的 Cookie,服务端如果只看 Cookie 不看 Origin,就会把连接当作合法用户。和 CSRF 的区别在于:CSRF 只能触发请求不能读取响应(同源策略限制),而 WebSocket 连接建立后恶意页面可以双向通信、读取服务端推送的敏感数据,危害更大。防护就是服务端必须校验 Origin 头。
URL 参数传 Token 和连接后发认证消息,哪个更安全?
连接后发认证消息更安全。URL 参数中的 token 会被记录在服务端访问日志、浏览器历史、代理日志中,且很难清除。连接后发消息的方式 token 只在 WebSocket 帧中传输,WSS 加密后中间人看不到。但连接后认证有一个窗口期:连接已建立但未认证,这段时间内服务端不能处理任何业务消息。实践中两者可以组合——URL 传短期一次性 ticket,连接后再用 JWT 换取长期会话。
WebSocket 上的 XSS 攻击怎么防?
WebSocket 传输的文本如果直接插入 DOM(innerHTML),和 HTTP 一样存在 XSS 风险。防护手段:消息内容做 HTML 转义后再渲染,或用 textContent 替代 innerHTML;服务端过滤消息中的 <script>、javascript:、onerror= 等危险模式;消息大小限制(如 10KB)防止超长 payload;消息结构白名单校验,只允许预定义的 type 字段。记住 XSS 的根源是渲染层,不是传输层。
怎么防止 WebSocket 连接被用于 DDoS?
服务端侧:限制单 IP 最大连接数(如 50),超出拒绝握手;单连接消息速率限制(如每分钟 100 条),超限断开;消息体积上限(如 64KB),防止内存炸弹;空闲连接超时断开(idleTimeout)。架构层:前端用 Nginx 做 WebSocket 代理,配置 limit_conn 和 limit_req;启用 DDoS 防护服务(Cloudflare 等);连接鉴权增加攻击成本,未认证的连接快速关闭不占资源。
javascript// 服务端安全校验示例 wss.on('connection', (ws, req) => { // 1. Origin 校验 const origin = req.headers.origin; if (!['https://yourdomain.com'].includes(origin)) { return ws.close(1003, 'Forbidden origin'); } // 2. 身份认证(首条消息) ws.authenticated = false; ws.once('message', (msg) => { const { type, token } = JSON.parse(msg); if (type !== 'auth') return ws.close(1008, 'Auth required'); try { const user = jwt.verify(token, SECRET); ws.authenticated = true; ws.userId = user.id; } catch { return ws.close(1008, 'Invalid token'); } }); // 3. 速率限制 ws.messageCount = 0; ws.on('message', () => { ws.messageCount++; if (ws.messageCount > 100) ws.close(1008, 'Rate limit'); }); });