CSWSH是WebSocket握手阶段最常被忽略的高危漏洞,本质为浏览器自动携带Cookie发起跨站连接,需校验Origin白名单、使用一次性绑定上下文的Token,并禁用URL传参及ws://明文协议。

WebSocket 本身不是漏洞,但常见实现方式会让它成为攻击跳板。不加固的 WebSocket 服务,基本等于把后端 API 的门钥匙直接贴在玻璃门上。
CSWSH(跨站 WebSocket 劫持)怎么验证和拦截
这是最常被忽略、也最容易被利用的漏洞。本质是:浏览器自动携带 Cookie 发起握手,攻击者诱导用户访问恶意页面,悄悄连上你的 wss://example.com/ws,复用合法会话窃取数据。
-
Origin头必须校验,但不能只靠它——它可被伪造,仅作初步过滤;服务端应硬编码白名单,例如只允许https://app.example.com和https://admin.example.com - 拒绝在 URL 查询参数里传 token,比如
wss://example.com/ws?token=xxx,该参数可能进 Nginx 日志、CDN 缓存或 Referer;改用Authorization: Bearer xxx请求头或升级前的 Cookie - Go 的
gorilla/websocket中,CheckOrigin回调必须返回false拒绝非法来源,不能留空或写return true - 前端建立连接时,
new WebSocket()的 URL 必须是静态字符串,禁止拼接用户输入(如location.search或 DOM 内容),否则可能触发路径遍历或协议降级
握手阶段的认证 Token 怎么设计才安全
一次性、短时效、绑定上下文的 token 是防 CSWSH 的核心。它不是用来“隐藏”地址,而是让每次连接都需动态授权。
WebSocket 8.18.2 是该协议规范的一个重要迭代版本,主要优化了连接稳定性与数据传输效率。它通过全双工通信机制,允许客户端与服务器在单一长连接上实时交换数据,大幅降低传统 HTTP 轮询的开销。该版本增强了心跳保活、自动重连及二进制帧传输能力,适用于即时通讯、在线游戏及金融行情推送等低延迟场景,为开发者提供更可靠的实时网络交互基础。
- token 应由 HTTPS 接口签发,有效期建议 ≤ 60 秒,且只能使用一次;服务端校验后立即失效(例如 Redis
DEL+SETNX原子操作) - token 载荷中必须包含用户 ID、时间戳、IP 摘要(如
sha256(clientIP + secret)),防止重放和跨 IP 复用 - 不要用 JWT 做长期连接凭证——JWT 过期前都有效,一旦泄露即全程可控;更适合做初始认证,后续连接应换用短期 session token
- 若用 Nginx 做反向代理,确保
proxy_set_header Authorization $http_authorization透传请求头,否则后端永远拿不到 token
wss:// 配置错误会导致什么后果
没配对的 wss:// 等于没加密。很多团队以为上了 TLS 就万事大吉,结果证书链断、SNI 不匹配、或服务端仍监听 ws://,导致中间人可直接解密所有帧。
- 服务端必须关闭
ws://监听,或强制 301 重定向到wss://;Node.js 的ws库默认不支持重定向,需自己拦截并返回 301 - 证书必须由可信 CA 签发,禁用自签名、Let’s Encrypt staging 证书;浏览器控制台出现
NET::ERR_CERT_AUTHORITY_INVALID时,连接会被静默拒绝 - TLS 版本至少为 1.2,OpenSSL 配置中明确禁用
SSLv3、TLSv1、TLSv1.1;用openssl s_client -connect example.com:443 -tls1_1可手动测试弱协议是否仍开放 - 前端
WebSocket构造函数必须写死wss://,不能根据环境变量拼接;否则开发环境用ws://localhost,上线后忘记切,就裸奔了
消息层还有哪些容易被绕过的风险点
就算握手和传输都安全,消息内容本身仍是高危区。XSS、注入、越权、敏感字段明文回传,全发生在这一层。
- 服务端收到文本帧后,必须做 UTF-8 合法性校验(避免无效字节触发解析崩溃),再做 JSON 解析;二进制帧需限制最大长度(如
MAX_PAYLOAD_SIZE = 1024 * 1024),超长直接close(1009) - 任何用户可控字段(如
message、room_id、target_user)都要走白名单校验,禁止正则宽松匹配(如/^[a-z0-9_-]+$/i允许-,但未限制长度,可能引发 DoS) - 服务端推送消息前,必须再次查库确认发送方权限(不能只信握手时的 session);例如管理员频道消息,不能仅靠连接时的 role 字段,而要实时 check
user.has_permission("send_to_admin_room") - 前端渲染消息时,必须 HTML 转义(如
textContent赋值),不能用innerHTML;即使后端做了转义,也要防客户端绕过——因为攻击者可直接用 Websocat 或 Burp 发 raw frame
真正难的不是加多少层防护,而是每层之间是否连贯:握手认证的用户身份,能不能带到消息处理上下文里;wss 的加密终点,是不是真的止于你的应用层,而不是某个中间代理;token 生效的那一刻,有没有同步更新风控系统的设备指纹。这些断点,才是攻击者真正下钩的地方。

















