🐟 捕鱼服务端安全审计 · 技术报告

客户端请求篡改 / 绕过 / 资损 / 可用性 · 防御性安全评估(全量技术版)
评估日期 2026-10-07 · 范围:登录鉴权服 / 网关 / 游戏逻辑服 · 方法:9 轴并行 + 独立复核 + 测试环境运行时实证
目录 结论风险总览P0 身份伪造 P1 跨款钱包资损P1 可用性/DoSP1 命中鱼种 有防护项修复优先级方法与可信度

〇、结论

核心玩法的派彩是服务端权威的、整体稳健——客户端篡改请求无法伪造命中结果、无法直接改余额、无法重放双花,十余个攻击面经代码审计+数据量化确认有防护。 但存在 1 个高危(P0)身份层缺口(已在测试环境端到端实证)与 5 类中危(P1),其中 1 条为真实资损路径(跨款并发钱包时序)。 最优先修复为登录凭证签名 + 网关握手前鉴权,一处即可封堵身份伪造、顶号骚扰,并把多数 DoS 挡在建连之前。
1
P0 高危
5
P1 中危(含1资损)
10+
已验证有防护
9
独立核查轴

证据标记:已验 代码/运行时实证   推断 代码结构已验、利用链待运行时复现

一、风险总览

级别问题业务影响证据
P0玩家身份可伪造
登录凭证无签名、网关不校验
冒充任意玩家登录、进房、读其余额、以其身份操作;测试环境证实可从外网做到已验 运行时
P1 资损跨款并发钱包时序缺口(TOCTOU)同账户同时在两款下注,"同一本金押多注",长期使赔付期望偏向玩家、公司缓慢失血推断 代码已验
P1可用性 / 拒绝服务
应用层限流只统计不拦、全局队列无界
构造异常/高频请求拖慢或影响全服稳定,干扰正常玩家;不直接造成资损已验 代码
P13 款命中目标可被客户端指定可指定攻击鱼种(含不在场)。台架量化证 RTP 中性、无法套利,属完整性/风控问题,非资损已验 台架
P2下注额无档位白名单可发任意正值下注绕过投注档风控;RTP 中性不白嫖已验
P3网关握手空指针异常特制畸形凭证触发,被框架兜住不崩服,可静默刷连接/日志已验

二、P0 · 玩家身份可伪造(最高危)

机理

登录服签发给客户端的身份凭证是明文 JSON 经 Base64 编码、无签名、无有效期(flogin_http_utils.go:66-79)。网关在建立连接时直接解码即信任其中的用户 ID、不验签、不查 Redis(net_ws_conn_mt_client.go:117-139);逻辑服再全盘信任网关传入的身份(net_conn_manager_proxy.go:33)。逻辑服另有的 token 比对只挂在存档定时器、不在登录路径,且比的是 Redis↔Redis、与客户端凭证无关 —— 拦不住伪造。

运行时实证(测试环境 gl7s,真实数据)

# 从一台外部公网机器(不在服务内网)用自构造凭证冒充另一真实玩家 探针侧: login=true LR_SUCCESS coin=99900.00 join=true frames=6 # 读到受害号真实余额 服务端: 16:27:43 登录成功 playerID=1600296 16:27:44 玩家进入房间 roomId=10 DeskId=52 seat=0 负样本: 仅"格式非法的 Base64"被拒 → 反证网关"解码即信"

网关端口绑 0.0.0.0、经 docker-compose 映射至宿主,外部公网机 TCP 直连成功 ⇒ 该缺口在测试环境任意互联网用户可利用。全程仅"登录+进房+读余额"即止,未动用任何资金。

⚠️ 待运维确认:生产正式服云安全组是否同样对公网开放相关端口?如与测试环境同配,则生产同样可被外部利用。本次审计未对正式服做任何连接。

三、P1 · 跨款并发钱包资损(TOCTOU)

机理

同一玩家跨多款游戏共享一个钱包(Redis GL:user),但开炮时的"余额够不够"判定走的是每款各自内存中的余额快照,真实扣款仅在约 1 秒后异步落库;而本应阻止"同一账户在多款同时在线"的互斥函数 GetOnlineGameType(db_online.go:52)在整个代码库中零调用(已核)。钱包另有"余额不可为负"的硬门(fish_balance_update.lua:161)。

可利用路径与后果

同账户同时在两款各开一局,双方都基于同一份"余额 100"的内存快照下注 → 可拿到多条只押了一份本金的独立中奖线;赢的照常入账,而亏穿本金的差额被"余额不可为负"拒绝并丢弃 ⇒ 赔付期望向玩家偏移,公司缓慢失血。此为经典 TOCTOU 多会话钱包弱点(已上网印证业界案例)。此前的同会话串行审计未覆盖此跨执行体面。

证据级别:代码结构已验(互斥函数零调用 + 异步落库),利用链为推断、尚未在测试环境实跑复现获利。注:简单"接通 GetOnlineGameType"不足以修复——其为"先查后判"本身即竞态,需改原子占位式互斥。

四、P1 · 可用性 / 拒绝服务

缓解边界(已有防护,不必过度担心):单业务帧上限 1MB、WebSocket 消息上限 4MB;业务 panic 逐帧隔离、不杀 goroutine、不塌整服;actor 邮箱有界做背压;跨执行体调用 8 秒超时,无永久死锁。

五、P1 · 3 款命中目标可被客户端指定(非资损)

机理

JLOH / JLRS / JLBL 三款在生产态下,当客户端提交的鱼种配置 ID 非 0 时,服务端不校验该鱼是否在场,直接以客户端给的鱼种计算倍率结算(fpool_impl.go:56,代码注释明示"兼容以鱼种参数判鱼的游戏 JLOH/JLBL/JLRS")。客户端可点名任意鱼种、甚至对不存在的鱼开火。

量化:是否能套利(台架 50 万发/组)

# 命中率 = RTP ÷ 倍率(algo_rtp_utils.go:39-78);判命中与派彩同一倍率 ⇒ 点任何鱼期望不超目标线 目标 RTP ≈ 0.985 JLOH 高倍鱼 0.964 ~ 0.986 JLRS 鳄鱼 0.9405 JLBL 鳄鱼 0.906 无一组超过目标线;7 月 1000 万发压测 0.979~0.991 旁证

结论:可点名鱼种=完整性/风控问题(可打不在场鱼、刷高倍广播、绕鱼池存在性风控),但期望上赚不到钱、非资损。

修复注:这三款的鱼由前端按随机种子自行生成、服务端鱼池只是影子,"命中前校验目标必须在场"会误拒合法命中,需改分层方案(鱼种白名单 + 已击杀去重 + 高倍频控,根治需服务端按种子复算或改服务端出鱼)。

六、确认有防护的核心环节

攻击者最常尝试的方向,经代码审计 + 数据量化确认目前有防护,客户端篡改请求无法得手:

七、修复优先级建议

优先级动作收益
最高登录凭证加签名(HS256)+有效期;网关握手升级前校验签名/有效期、核对服务端记录(仓内已有同口径现成实现可复用 rebate_reader.go:74-127,不改客户端、不改凭证格式);同批:凭证不再写入日志、补空指针判空一处封堵身份伪造(P0)+顶号骚扰,并把多数 DoS 挡在建连前
最高网关端口收敛为仅内部入口可达,不对公网直出;收紧云安全组消除"任意外部人可直接攻击"的前提
高跨款钱包改原子占位式互斥(非简单接通在线判定);拒负单独告警+停火;二期按预留额度模型预扣(RFC 4006 / GLI-19 §4.3.3b)消除唯一资损路径
中网关入口加限流(并发连接+速率,前提先修真实 IP 还原)+全局连接上限+慢连接超时降低 DoS 风险(入口限流间接给无界队列封顶)
中3 款命中改分层校验(鱼种白名单+已击杀去重+高倍频控);下注额限定配置档位白名单(数据源现成 m_bet_list)消除完整性/风控缺口

八、审计方法与可信度

⚠️ 审计范围外的附带发现:测试环境在网关/网关入口之外另有对公网的暴露面(至少一处判为高危),仅做了无凭证连通性探测、未利用。建议运维对测试环境与正式服的云安全组及入口配置做一次全面核对。