代码审查结论 · 2026-10-09 10:30

承接 02:00 的《代码审查报告》。这一轮把上轮标为「最大缺口」的鉴权 56 处判完了。
纪律不变:每条结论带判读依据,不抽样、不外推。

一、鉴权:530 路由逐层判读完成

上一轮的错:把 530 个路由笼统报成「59 疑似」,没区分读写。

本轮先分层,再逐个判。

分层结果

层数量处理
附近有鉴权关键字471✅ 正常
无鉴权关键字63↓继续分层
├ 写操作(POST/PUT/DELETE/PATCH)12🔴 逐个判读(12/12)
└ 读操作(GET)51✅ 只读,风险低(不逐个展开)

🔴 12 个写操作逐个判读结论

端点结论依据
auth.login / password_login / admin_login✅ 合理无鉴权登录入口本身就要匿名
miniapp_pay.pay_notify✅ 安全有验签:wxpay_notify.verify_signature(),失败 raise 401(fail-closed,不写库、不留脏数据)
miniapp_pay.refund_notify✅ 安全同上
packages.package_pay_notify✅ 安全有 签名验真
packages.package_refund_notify✅ 安全有 签名验真
merchant_console.service_fee_notify✅ 安全有签名 + 随机数双重验真
stalls.create_stall_lead✅ 合理公开留资入口(留客服线索,本就要匿名)
miniapp_voice_guide.report_play🟡 建议加固匿名可调、无鉴权无限流 ⇒ 播放量可被刷
auth.workbench_exchange🟡 待你确认「凭据换 token」型接口,需确认凭据来源是否可控
miniapp_voucher.resolve_phone✅ 死代码,不可利用第一行 raise HTTPException(410, "已废弃") ⇒ 一进来就拒绝

###🔴 支付回调我额外查了一个致命问题:沙箱开关

「沙箱模式能否在生产上被打开、导致任意请求可伪造支付成功?」 —— 逐行看了:

def _is_sandbox() -> bool:
    """沙箱模式判定:env 读 WXPAY_SANDBOX=true"""
    return os.environ.get("WXPAY_SANDBOX", "").lower() == "true"

线上容器实测:WXPAY_SANDBOX 未设置 ⇒ 非沙箱 ⇒ 走验签路径。✅ 安全。

🔴 但我要指出一个设计上的脆弱点:

沙箱判定虽然已经修过一次(代码注释里写着

335 五幕波波发现:沙箱标志只认服务端配置(原 body.get("sandbox") 旁路=任意请求可跳验签——P0 修复)),

✅ 那个 P0 确实修好了(现在只认 env,不认 body)。

⚠️ 但它仍是「一个环境变量决定安全等级」 ⇒

将来若运维误设 WXPAY_SANDBOX=true 到生产,所有支付回调的验签会被整体关闭。

建议:WXPAY_SANDBOX=true 时要求同时满足「非生产环境」才生效

(例如再判一个 APP_ENV=prod 就强制 fail-closed)。这条我不擅自改,等你拍板。


二、SQL 注入:上轮已逐处判完,结论不变

99/99 处逐处追溯变量来源 → 高危 0 处。

判定数量
常量(赋值全是硬编码字面量)47
动态(由代码拼接,值走参数化)41
高危0
待查(追不到来源)8

⚠️ 判断局限(不可忽略):47 处「常量」只查了本文件内的赋值行,

跨文件重新赋值的情况脚本看不到 ⇒ 结论强度中高,非绝对。


三、分页注入:全安全

limit/offset/page/per_page 全部 int 类型 + 范围约束,

全仓 0 个 str 型排序参数 ⇒ 不存在排序字段注入。

这是本项目做得最好的一处,建议作为范式推广。


四、修改方案

🔴 建议改(按性价比排序)

#改动理由风险
1report_play 加限流(按 device_id_hash 或 IP)匿名无限流,播放量可被刷,污染运营数据低
2WXPAY_SANDBOX 加生产环境二次确认一个环境变量决定全站支付验签开关,误设即全线失守低
38 处「待查」逐个定位脚本追不到来源,可能藏着跨文件赋值低
4删掉 resolve_phone 死代码一进来就 410,留着只会让人误判极低
5SQL 99 处分批改参数化当前不构成注入,属脆弱写法中(改动面大)

✅ 建议保留 / 推广

⏸ 待你确认(我不擅自改)

事项我的建议
workbench_exchange 的凭据来源需你说明凭据怎么来的,我再判
report_play 的限流口径建议按 device_id_hash 而非 IP(小程序场景 IP 会变)

五、本轮仍然没覆盖的(诚实标注)

未覆盖为什么重要
🔴 并发/事务/幂等可能超卖、重复扣款。记忆里 06-30 误取消 6 笔订单、方案 A 单 6274-6277 待裁定 都与此相关
🔴 金额精度有没有浮点混进分
⚠️ 51 个读操作未逐个展开只读接口,风险显著低于写操作
⚠️ 跨文件赋值(SQL 常量判定局限)47 处结论强度中高
🔴 wxss/wxml/js 13,426 行前端渲染问题查不到

**⇒ 这份结论可以用于「注入 + 鉴权」两个维度的决策,

但不能当作「整体代码质量已审查」。**

六、给下一轮的一句话

「12 个写操作全部判读」比「530 个路由扫过一遍」更有价值。
🔴 因为写操作才是能被利用的,读操作最多是信息泄露。
分层比全量更省力,也更抓重点。