[C-05][medium] 每端请求限速只作用于 send,撤回、状态、目录、群、unlock 等请求都不限速 #36

Closed
opened 2026-09-30 13:56:59 +08:00 by nixevol · 1 comment
Owner

编号:C-05 严重级:medium 工作线:消息核心(internal/app/message、serve 的 messageLoops) 来源:审查 M-08、I-10、P-11
依赖:无 被依赖:无

结论与统一方案

三份审查结论一致。统一方案:message 包导出 AllowRequest(编号),与 send 共用同一个桶;appUplink.HandleUplink 在解码之后、分发之前,对 Ack、ReceiptAck 以外的所有帧调用它,超限回 rate_limited;同时去掉 Submit 里的检查,避免 send 被计两次。同步更新 DEVIATIONS M1 第 2 条。

开放注册后任何人都能拿到一个端,现在可以不限速地发 unlock(每次一次 argon2)、presence.get(每次最多 200 次查库)、directory.list 等,挤占哈希池与数据库。

改动文件

internal/app/message/rate.go、submit.go;cmd/nixmsg/uplink.go(HandleUplink)。

与其他问题的交互 / 冲突说明

  • uplink.go 只改 HandleUplink:生命周期函数归 B-09,publishResp 归 B-06。
  • submit.go 只删限速检查:校验归 C-07,对话密码函数归 U-03。
  • 消息线里直接调 Submit 验证限速的测试迁到上行层,或加测试开关。

验收与测试

  • 1 秒内发 150 个 status / directory.list / unlock,超过约 100 个之后返回 rate_limited。
  • ack、receipt_ack 不计入;send 与其他请求共用配额。

问题明细(各区审查原文,证据含文件与行号)

以下是本次复审各区审查报告的原文段落。A、M、I、P、S 开头的是原始发现编号(A 管理后台与网页、M 消息核心、I 身份认证群在线、P 传输平台部署、S SDK)。解决方案以本 issue 上方的"结论与统一方案"为准;原文里的方案与之不一致时,按上方执行。

[M-08] 请求频率只限制 send,撤回、状态、目录、群等请求不限

  • 严重级:medium
  • 分类:与 PRD 不符 / 安全
  • 现象与影响:
    • 频率桶只在 Submit 里检查。
    • recall、status、directory.list、presence.、group.、self.*、unlock 全部不计数。
    • 开放自助注册后,任何注册账号都可以无限速地拉目录、查状态,给数据库读连接和 CPU 施压。
  • 证据:submit.go:23-26;uplink.go:80-223 的分发里没有任何限流;全仓 rates.allow 只有这一处。
  • 文档依据:PRD F05「每个端默认每秒最多 50 个请求(可突发到 100,确认类请求不算)」;DEVELOPMENT 6.10「除 ack、receipt_ack 外的请求共用一个桶」。
  • 为何不是故意设计:DEVIATIONS M1 第 2 条只说明桶当时挂在 Submit 入口(那时上行还没接线),并把「由上行统一限流」列为备选。偏差说明也不能改变 PRD 规定的产品行为。
  • 解决方案:
    1. message 包导出 AllowRequest(编号)。
    2. appUplink.HandleUplink 在分发前对 ack、receipt_ack 以外的所有帧调用它,超限回 rate_limited。
    3. 去掉 Submit 里的检查,避免 send 被计两次。
  • 改动文件:internal/app/message/rate.go、submit.go;cmd/nixmsg/uplink.go(总控线)。
  • 交互/冲突风险:SDK 对非 send 请求收到 rate_limited 会直接返回给应用,符合 DEVELOPMENT 第 9 节。
  • 需补测试:1 秒内发 150 个 status:前约 100 个成功,其余返回 rate_limited;ack 不计入。
  • 置信度:代码阅读确定。

[I-10] 每端请求限速只作用于 send

  • 严重级:medium
  • 分类:安全 / 与文档不符
  • 现象与影响
    • 限速桶只挂在 message.Submit 上;unlock、self.、group.、presence.、directory. 都不限速,recall 和 status 也不限(属消息线)。
    • 开放注册后任何人都能拿到一个端,可以不限速地发 unlock(每次一次 argon2)、presence.get(每次最多 200 次查库)、带大量成员的 group.create,挤占 argon2 池和数据库。
  • 证据:message/submit.go:24 是唯一的限速检查;cmd/nixmsg/uplink.go:64-78 分发前不检查;DEVIATIONS M1 第 2 条写明备选方案是「在上行统一限流」。
  • 文档依据:DEVELOPMENT 6.10(591)「除 ack、receipt_ack 外的请求共用一个桶」;PRD F05(196)。
  • 为何不是故意设计:M1 把桶放在 Submit 时预期由上行统一限流,但接线时没有补。
  • 解决方案:消息线导出 AllowRequest(ep),用同一个桶;appUplink.HandleUplink 在分发前对 Ack 和 ReceiptAck 以外的帧检查,超出返回 rate_limited;同时去掉 Submit 里的检查,避免 send 被计两次。
  • 改动文件:cmd/nixmsg/uplink.go、message/rate.go、message/submit.go。
  • 与其他模块的交互/冲突风险:消息线里直接调 Submit 验证限速的测试需要迁到上行层,或加一个测试开关。
  • 需补测试:1 秒内发 150 个 unlock,超过 100 个之后返回 rate_limited;ack 不计数。
  • 置信度:代码阅读确定。

[P-11] 上行限流只覆盖 send,其余请求都不限速

  • 严重级:medium
  • 分类:与文档不符 / 安全
  • 现象与影响:
    • 限流的令牌桶只挂在 message.App.Submit 上。
    • appUplink.dispatch 分发的 directory.list、status、group.get(每次最多 200 行)、presence.*、self.update、recall、unlock 等请求都不经过任何限流。
    • 已登录的端,包括开放注册后任何拿到 App 里注册安全码的人,可以用这些请求打满读库和全局写队列,影响所有端。
  • 证据:uplink.go:64-224;message/submit.go:25。
  • 文档依据:DEVELOPMENT 6.10「除 ack、receipt_ack 外的请求共用一个桶,默认每秒 50、突发 100」。
  • 为何不是故意设计:DEVIATIONS M1 第 2 条记录的是桶挂在 Submit、突发容量写死,备选方案里提到"由连接线在上行统一限流",接线时没有补上。
  • 解决方案:在 appUplink.HandleUplink 解码之后、分发之前统一扣桶,ack 和 receipt_ack 除外,超限回 rate_limited。为了保持"共用一个桶"又不重复扣 send,二选一:
    • message 暴露现有的限流器(例如 AllowRequest(endpointID)),uplink 对非 send 请求调它,send 仍由 Submit 自己扣;
    • 或者把桶整体挪到 uplink,Submit 里去掉。
  • 改动文件:cmd/nixmsg/uplink.go;需要 M 线配合改 internal/app/message/rate.go、submit.go
  • 与其他模块的交互/冲突风险:需要和 M 线约定桶的归属,避免同一请求被扣两次。
  • 需补测试:同一端 1 秒内发 150 个 directory.list,至少 50 个收到 rate_limited;ack 不受限制;send 和其他请求共用配额。
  • 置信度:代码阅读确定

复审基线:main 4059a15(2026-09-30)。编号说明、各工作线的合并顺序、共享文件归属见总览 #7。

**编号**:C-05 **严重级**:medium **工作线**:消息核心(internal/app/message、serve 的 messageLoops) **来源**:审查 M-08、I-10、P-11 **依赖**:无 **被依赖**:无 ### 结论与统一方案 三份审查结论一致。统一方案:message 包导出 `AllowRequest(编号)`,与 send 共用同一个桶;`appUplink.HandleUplink` 在解码之后、分发之前,对 Ack、ReceiptAck 以外的所有帧调用它,超限回 `rate_limited`;同时去掉 `Submit` 里的检查,避免 send 被计两次。同步更新 DEVIATIONS M1 第 2 条。 开放注册后任何人都能拿到一个端,现在可以不限速地发 unlock(每次一次 argon2)、`presence.get`(每次最多 200 次查库)、`directory.list` 等,挤占哈希池与数据库。 ### 改动文件 `internal/app/message/rate.go`、`submit.go`;`cmd/nixmsg/uplink.go`(`HandleUplink`)。 ### 与其他问题的交互 / 冲突说明 - uplink.go 只改 `HandleUplink`:生命周期函数归 B-09,`publishResp` 归 B-06。 - submit.go 只删限速检查:校验归 C-07,对话密码函数归 U-03。 - 消息线里直接调 `Submit` 验证限速的测试迁到上行层,或加测试开关。 ### 验收与测试 - 1 秒内发 150 个 status / directory.list / unlock,超过约 100 个之后返回 `rate_limited`。 - ack、receipt_ack 不计入;send 与其他请求共用配额。 --- ### 问题明细(各区审查原文,证据含文件与行号) > 以下是本次复审各区审查报告的原文段落。A、M、I、P、S 开头的是原始发现编号(A 管理后台与网页、M 消息核心、I 身份认证群在线、P 传输平台部署、S SDK)。**解决方案以本 issue 上方的"结论与统一方案"为准**;原文里的方案与之不一致时,按上方执行。 #### [M-08] 请求频率只限制 send,撤回、状态、目录、群等请求不限 - 严重级:medium - 分类:与 PRD 不符 / 安全 - 现象与影响: - 频率桶只在 `Submit` 里检查。 - recall、status、directory.list、presence.*、group.*、self.*、unlock 全部不计数。 - 开放自助注册后,任何注册账号都可以无限速地拉目录、查状态,给数据库读连接和 CPU 施压。 - 证据:`submit.go:23-26`;`uplink.go:80-223` 的分发里没有任何限流;全仓 `rates.allow` 只有这一处。 - 文档依据:PRD F05「每个端默认每秒最多 50 个请求(可突发到 100,确认类请求不算)」;DEVELOPMENT 6.10「除 ack、receipt_ack 外的请求共用一个桶」。 - 为何不是故意设计:DEVIATIONS M1 第 2 条只说明桶当时挂在 Submit 入口(那时上行还没接线),并把「由上行统一限流」列为备选。偏差说明也不能改变 PRD 规定的产品行为。 - 解决方案: 1. message 包导出 `AllowRequest(编号)`。 2. `appUplink.HandleUplink` 在分发前对 ack、receipt_ack 以外的所有帧调用它,超限回 `rate_limited`。 3. 去掉 `Submit` 里的检查,避免 send 被计两次。 - 改动文件:`internal/app/message/rate.go`、`submit.go`;`cmd/nixmsg/uplink.go`(总控线)。 - 交互/冲突风险:SDK 对非 send 请求收到 `rate_limited` 会直接返回给应用,符合 DEVELOPMENT 第 9 节。 - 需补测试:1 秒内发 150 个 status:前约 100 个成功,其余返回 `rate_limited`;ack 不计入。 - 置信度:代码阅读确定。 #### [I-10] 每端请求限速只作用于 send - **严重级**:medium - **分类**:安全 / 与文档不符 - **现象与影响** - 限速桶只挂在 `message.Submit` 上;unlock、self.*、group.*、presence.*、directory.* 都不限速,recall 和 status 也不限(属消息线)。 - 开放注册后任何人都能拿到一个端,可以不限速地发 unlock(每次一次 argon2)、`presence.get`(每次最多 200 次查库)、带大量成员的 `group.create`,挤占 argon2 池和数据库。 - **证据**:`message/submit.go:24` 是唯一的限速检查;`cmd/nixmsg/uplink.go:64-78` 分发前不检查;DEVIATIONS M1 第 2 条写明备选方案是「在上行统一限流」。 - **文档依据**:DEVELOPMENT 6.10(591)「除 ack、receipt_ack 外的请求共用一个桶」;PRD F05(196)。 - **为何不是故意设计**:M1 把桶放在 Submit 时预期由上行统一限流,但接线时没有补。 - **解决方案**:消息线导出 `AllowRequest(ep)`,用同一个桶;`appUplink.HandleUplink` 在分发前对 Ack 和 ReceiptAck 以外的帧检查,超出返回 `rate_limited`;同时去掉 Submit 里的检查,避免 send 被计两次。 - **改动文件**:`cmd/nixmsg/uplink.go`、`message/rate.go`、`message/submit.go`。 - **与其他模块的交互/冲突风险**:消息线里直接调 Submit 验证限速的测试需要迁到上行层,或加一个测试开关。 - **需补测试**:1 秒内发 150 个 unlock,超过 100 个之后返回 `rate_limited`;ack 不计数。 - **置信度**:代码阅读确定。 #### [P-11] 上行限流只覆盖 send,其余请求都不限速 - **严重级**:medium - **分类**:与文档不符 / 安全 - **现象与影响**: - 限流的令牌桶只挂在 `message.App.Submit` 上。 - `appUplink.dispatch` 分发的 directory.list、status、group.get(每次最多 200 行)、presence.*、self.update、recall、unlock 等请求都不经过任何限流。 - 已登录的端,包括开放注册后任何拿到 App 里注册安全码的人,可以用这些请求打满读库和全局写队列,影响所有端。 - **证据**:`uplink.go:64-224`;`message/submit.go:25`。 - **文档依据**:DEVELOPMENT 6.10「除 ack、receipt_ack 外的请求共用一个桶,默认每秒 50、突发 100」。 - **为何不是故意设计**:DEVIATIONS M1 第 2 条记录的是桶挂在 Submit、突发容量写死,备选方案里提到"由连接线在上行统一限流",接线时没有补上。 - **解决方案**:在 `appUplink.HandleUplink` 解码之后、分发之前统一扣桶,ack 和 receipt_ack 除外,超限回 `rate_limited`。为了保持"共用一个桶"又不重复扣 send,二选一: - message 暴露现有的限流器(例如 `AllowRequest(endpointID)`),uplink 对非 send 请求调它,send 仍由 Submit 自己扣; - 或者把桶整体挪到 uplink,Submit 里去掉。 - **改动文件**:`cmd/nixmsg/uplink.go`;需要 M 线配合改 `internal/app/message/rate.go`、`submit.go` - **与其他模块的交互/冲突风险**:需要和 M 线约定桶的归属,避免同一请求被扣两次。 - **需补测试**:同一端 1 秒内发 150 个 directory.list,至少 50 个收到 rate_limited;ack 不受限制;send 和其他请求共用配额。 - **置信度**:代码阅读确定 --- <sub>复审基线:main `4059a15`(2026-09-30)。编号说明、各工作线的合并顺序、共享文件归属见总览 #7。</sub>
nixevol added the P2-mediumlane/messagereview-2026-09-30 labels 2026-09-30 13:56:59 +08:00
Author
Owner

已合入 origin/main 0c9b459。落地提交 aa24e97 fix: 上行分发前统一限速,send 不再单独扣桶 (#36)。

已合入 origin/main `0c9b459`。落地提交 `aa24e97` fix: 上行分发前统一限速,send 不再单独扣桶 (#36)。
Sign in to join this conversation.