[P0][CR-S1] 联机中继:单个畸形帧头即可让进程崩溃(帧校验、上限、异常隔离缺失) #517
Loading…
Reference in New Issue
No description provided.
Delete Branch "%!s(<nil>)"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
问题描述
联机唯一的服务端组件是
scripts/net-relay.ts:191 行,手写 RFC 6455 帧编解码。任何能连上中继的客户端,只要发一个 10 字节的帧头,就能让整个 Node 进程以 exit 1 退出,所有玩家同时断线(已复现)。除此之外,中继对帧格式、连接数、缓冲和背压都没有约束,也不分配身份、不分房间。目前它只监听
127.0.0.1;一旦为了远程联机放到反代或隧道后面,这些问题会直接暴露。证据
decodeFrames直接throw new Error('frame too large'):net-relay.ts:99。socket.on('data')回调里被同步调用(net-relay.ts:150-155),回调外没有 try/catch。入口 net-server.ts:12-22 也没有任何进程级处理,异常未被捕获,进程就退出了。message监听器里 throw(transport.ts:383-384)。一个大帧会被原样广播,让所有客户端同时报错。peer.write(out)不看返回值(net-relay.ts:163-168),慢连接会让 Node 的写缓冲无限增长。upgrade不校验Origin、Sec-WebSocket-Version和路径,也没有连接数上限(net-relay.ts:135-148)。socket.on('error')静默吞掉错误(net-relay.ts:173)。复现
复现脚本 probe-relay-crash.mts(点击展开)
把脚本保存为仓库根目录下的
probe-relay-crash.mts,然后执行npx tsx probe-relay-crash.mts。脚本只 importscripts/net-relay.ts,不修改任何文件。实际输出如下;修复后应打印
relay still alive after 1s。根因
中继把"对端发来的数据不合法"当作编程错误抛出,而不是当作不可信输入处理。它是按"两个可信的浏览器互相转发"设计的(见 net-server.ts:1-9 的注释),但它实际上是一个监听 TCP 端口的服务。
修复指南
decodeFrames改为返回结构化错误(例如{ kind: 'error', closeCode: 1009 }),不要 throw。maxMessageBytes对齐(从src/net/transport.ts导出同一个常量),超限用 1009 关闭。peer.write()返回 false 时记录待发字节数,超过阈值就断开慢连接。Origin走 allowlist(做成配置项),并校验Sec-WebSocket-Version: 13。error事件至少在 verbose 模式下记录下来。如果 M20(D2GS 服务端联机)最终取代 P2P 锁步,第 5 步只做最小版本即可。但 #508 的 WS↔TCP 桥同样是服务端组件,应该直接满足第 1–4 步的要求。
验收标准
复现脚本打印
relay still alive,进程不退出;发起攻击的连接收到 1009 或 1002 后被关闭。新增基于真实 TCP 的中继健壮性测试,覆盖以下情况:
write返回 false)每种情况下中继都存活,其他连接不受影响。
一个恶意连接无法让其他客户端的
message监听器抛异常。中继转发出去的报文,peer 字节等于中继分配的序号。
npm run typecheck0 error;npx vitest run全部通过。相关
已在提交
7bc21df(fix(net): harden WebSocket relay frame validation and crash isolation (#517))中完成修复,并在5de27f8中补充端到端与 136 关全量验证套件。修复与验证摘要
rsv === 0、客户端masked === true、控制帧<= 125B、二进制帧 opcode 校验、预分配前maxPayloadBytes = 4096限制)、连接/房间/速率上限与单连接错误隔离,并在src/net/protocol.ts中对截断/未知帧安全拒收而不使进程崩溃。tests/p0-517-relay-hardening.test.tsnpm run build0 错误、14 个 P0/E2E 测试套件(190/190 用例)100% 通过、全仓 Vitest 6,460/6,460 通过、136/136 关无头浏览器巡检通过。