WebSocket 消息传输:帧格式、掩码机制与代理污染攻击防护

一、WebSocket 传递消息的核心 ------ 帧(Frame)格式

WebSocket 不像 HTTP 那样用 "请求 - 响应报文" 传输数据,而是将所有消息(文本 / 二进制)拆分成帧(Frame) 传输 ------ 哪怕是一条简单的 "你好",也会被包装成一个帧发送。

帧是 WebSocket 数据传输的最小单位,其格式遵循 RFC 6455 规范,结构如下(按字节顺序):

字段 位数 核心作用
FIN 1 标识是否为消息的最后一帧:1= 最后一帧(完整消息),0= 后续还有帧(分片)
RSV1/RSV2/RSV3 1*3 扩展位,默认全 0,仅启用扩展(如压缩)时使用
Opcode 4 帧类型:• 0x0:延续帧(分片消息的后续帧)• 0x1:文本帧(UTF-8 数据)• 0x2:二进制帧• 0x8:关闭帧• 0x9:PING 帧• 0xA:PONG 帧
MASK 1 标识数据是否被掩码:1= 客户端发送的帧必须掩码,0= 服务器发送的帧不能掩码
Payload length 7/7+16/7+64 负载数据长度:・0-125:直接表示长度・126:后续 2 字节表示长度・127:后续 8 字节表示长度
Masking-key 0/32 掩码密钥:仅 MASK=1 时存在(客户端帧必带),4 字节随机数
Payload data 可变 实际传输的数据(文本 / 二进制),若 MASK=1 需先解掩码

通俗解读帧格式(以客户端发 "你好" 为例)

  1. FIN=1:这是完整的 "你好" 消息,没有分片;
  2. Opcode=0x1:这是文本帧,数据是 UTF-8 格式;
  3. MASK=1:客户端发送的帧必须掩码,后续带 4 字节掩码密钥;
  4. Payload length=2:"你好" 的 UTF-8 编码是 2 个字节;
  5. Masking-key :比如随机生成 0x12 0x34 0x56 0x78
  6. Payload data :"你好" 的 UTF-8 二进制数据(0xE4 0xBD 0xA0 0xE5 0xA5 0xBD?注:实际 "你好" 是 6 字节,此处仅举例),先通过掩码密钥处理后再传输。

关键规则

  • 客户端→服务器的所有数据帧,MASK 必须为 1(强制掩码);
  • 服务器→客户端的所有数据帧,MASK 必须为 0(禁止掩码);
  • 若违反该规则,对方会直接断开连接。

二、核心:WebSocket 掩码机制(怎么加 / 解掩码?)

掩码的本质是客户端用 4 字节随机密钥,对负载数据做简单的异或(XOR)运算,目的是 "打乱" 数据二进制,防御代理污染攻击。

1. 掩码运算规则(固定算法)

假设:

  • 掩码密钥(Masking-key):4 字节数组 mask[0], mask[1], mask[2], mask[3]
  • 负载数据(Payload data):字节数组 data[0], data[1], ..., data[n-1]
  • 掩码后的数据:encrypted_data[i] = data[i] XOR mask[i mod 4]
  • 解掩码:decrypted_data[i] = encrypted_data[i] XOR mask[i mod 4](和加密是同一个运算)。

2. 示例:简单掩码 / 解掩码计算

比如:

  • 掩码密钥:[0x12, 0x34, 0x56, 0x78]
  • 原始数据(1 字节):0xE4
  • 掩码计算:0xE4 XOR 0x12 = 0xF6(i=0,i mod 4=0,用 mask [0]);
  • 解掩码:0xF6 XOR 0x12 = 0xE4(还原原始数据)。

3. 为什么只有客户端帧需要掩码?

因为代理污染攻击的风险只存在于 "客户端→服务器" 的方向(后文详解),服务器→客户端的帧无需掩码,可减少运算开销。

三、防护目标:代理污染攻击(为什么需要掩码?)

1. 先理解:什么是 "HTTP 代理 / 缓存污染"?

早期互联网中,大量中间节点(如 HTTP 代理、缓存服务器、反向代理)是为 HTTP 协议设计的 ------ 这些节点会 "无脑" 解析二进制数据,试图识别 HTTP 报文的特征(比如 \r\nHost:GET 等),并缓存 / 转发数据。

如果 WebSocket 客户端直接发送未掩码的明文二进制数据,可能会出现:

  • 数据中恰好包含 HTTP 报文的特征字符串(比如 GET / HTTP/1.1\r\n);
  • 中间代理服务器误将 WebSocket 帧识别为 "HTTP 请求";
  • 代理服务器缓存这个 "伪 HTTP 请求",并将其返回给后续访问该代理的用户 ------ 这就是代理污染攻击(也叫 "缓存污染")。

2. 代理污染攻击的具体场景(无掩码时的风险)

假设客户端通过 WebSocket 发送一条文本消息:"GET /admin HTTP/1.1\r\nHost: hack.com\r\n"(只是普通聊天内容,无攻击意图):

  • 若没有掩码,这条消息的二进制数据会原封不动通过代理;
  • 代理服务器识别到 GET /admin HTTP/1.1 这个 HTTP 特征,误以为这是一个 HTTP 请求;
  • 代理服务器会缓存 "/admin 路径对应 hack.com 的响应";
  • 后续其他用户通过该代理访问 http://正常网站/admin 时,代理会返回缓存的 hack.com 内容 ------ 导致正常网站的缓存被污染,用户访问到错误数据。

3. 掩码如何防御代理污染攻击?

掩码通过 "随机异或打乱数据二进制",确保:

  • 客户端发送的 WebSocket 帧数据中,几乎不可能出现连续的 HTTP 特征字符串 (比如 GET\r\nHost:);
  • 中间代理服务器无法识别出 HTTP 报文特征,也就不会误解析、误缓存;
  • 即使数据中原本包含 HTTP 特征,掩码后也会被打乱,彻底避免代理污染。

关键补充:掩码不是加密

  • 掩码的密钥是 4 字节随机数,且和数据一起传输(Masking-key 字段),任何人拿到帧数据和密钥都能解掩码;
  • 掩码的唯一目的是 "打乱数据特征",而非 "保护数据安全"(数据安全需靠 WSS 加密);
  • 服务器收到掩码帧后,会先用 Masking-key 解掩码,再处理数据 ------ 这个过程对开发者透明(ws 库、浏览器会自动处理)。

四、代码层面的体现(掩码是透明的)

你之前的示例代码中,没有手动处理掩码,因为:

  • 客户端(浏览器) :调用 ws.send("你好") 时,浏览器会自动:
    1. 生成 4 字节随机掩码密钥;
    2. 对 "你好" 的 UTF-8 数据做掩码运算;
    3. 按帧格式封装数据,设置 MASK=1,携带 Masking-key;
    4. 发送帧到服务器。
  • 服务端(ws 库) :收到帧后,会自动用 Masking-key 解掩码,你在 ws.on('message') 中拿到的是还原后的明文 "你好"------ 无需手动处理。

验证:强制关闭掩码会怎样?

如果通过底层编程(如手动构造帧)发送 MASK=0 的客户端帧,服务器会直接断开连接,并抛出类似 "Received unmasked frame from client" 的错误 ------ 这是协议强制要求。

五、核心总结

  1. WebSocket 消息格式:所有消息以 "帧" 为单位传输,帧包含类型、长度、掩码标识、密钥、数据等字段,客户端帧必须掩码,服务器帧禁止掩码;
  2. 掩码机制:客户端用 4 字节随机密钥对数据做异或运算,服务器收到后反向还原,过程对开发者透明;
  3. 掩码的唯一目的:打乱数据二进制特征,防止中间 HTTP 代理 / 缓存服务器误识别为 HTTP 报文,从而避免代理污染攻击;
  4. 关键区别:掩码≠加密(无安全保护),WSS(WebSocket over TLS)才是加密传输,掩码仅解决协议兼容问题。

0voice · GitHub

相关推荐
Geometry Fu1 天前
《无线传感网络》WSN 第3讲 MAC协议 知识点总结+习题讲解
网络·网络协议·无线传感器网络·wsn
hxjhnct1 天前
https,WebSocket,tcp的关系
网络协议·tcp/ip·https
qq_310658511 天前
webrtc源码走读(五)核心引擎层——传输模块
服务器·网络·音视频·webrtc
27669582921 天前
美团websocket 分析
websocket·网络协议·npm·美团·mtgsig·美团websoket·美团商家端
hgz07101 天前
内存、磁盘、网络监控
网络
Xの哲學1 天前
Linux SLUB 内存分配器深度剖析: 从设计哲学到实战调试
linux·服务器·网络·算法·边缘计算
ICT系统集成阿祥1 天前
基于路由器的串口数据 IP 封装传输配置手册
网络·网络协议·tcp/ip
天上飞的粉红小猪1 天前
网络基础概念
linux·服务器·网络·c++
Salt_07281 天前
DAY 54 对抗生成网络
网络·python·神经网络·机器学习·计算机视觉