承接 02:00 的《代码审查报告》。这一轮把上轮标为「最大缺口」的鉴权 56 处判完了。
纪律不变:每条结论带判读依据,不抽样、不外推。
上一轮的错:把 530 个路由笼统报成「59 疑似」,没区分读写。
本轮先分层,再逐个判。
| 层 | 数量 | 处理 |
|---|---|---|
| 附近有鉴权关键字 | 471 | ✅ 正常 |
| 无鉴权关键字 | 63 | ↓继续分层 |
| ├ 写操作(POST/PUT/DELETE/PATCH) | 12 | 🔴 逐个判读(12/12) |
| └ 读操作(GET) | 51 | ✅ 只读,风险低(不逐个展开) |
| 端点 | 结论 | 依据 |
|---|---|---|
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)。这条我不擅自改,等你拍板。
99/99 处逐处追溯变量来源 → 高危 0 处。
| 判定 | 数量 |
|---|---|
| 常量(赋值全是硬编码字面量) | 47 |
| 动态(由代码拼接,值走参数化) | 41 |
| 高危 | 0 |
| 待查(追不到来源) | 8 |
⚠️ 判断局限(不可忽略):47 处「常量」只查了本文件内的赋值行,
跨文件重新赋值的情况脚本看不到 ⇒ 结论强度中高,非绝对。
limit/offset/page/per_page 全部 int 类型 + 范围约束,
全仓 0 个 str 型排序参数 ⇒ 不存在排序字段注入。
这是本项目做得最好的一处,建议作为范式推广。
| # | 改动 | 理由 | 风险 |
|---|---|---|---|
| 1 | report_play 加限流(按 device_id_hash 或 IP) | 匿名无限流,播放量可被刷,污染运营数据 | 低 |
| 2 | WXPAY_SANDBOX 加生产环境二次确认 | 一个环境变量决定全站支付验签开关,误设即全线失守 | 低 |
| 3 | 8 处「待查」逐个定位 | 脚本追不到来源,可能藏着跨文件赋值 | 低 |
| 4 | 删掉 resolve_phone 死代码 | 一进来就 410,留着只会让人误判 | 极低 |
| 5 | SQL 99 处分批改参数化 | 当前不构成注入,属脆弱写法 | 中(改动面大) |
int + 范围约束的分页参数 —— 从类型层杜绝注入ALLOWED_FOLDERS + 拒绝 ..)code_quality_scan.py 的覆盖率自述(防止报告夸大)| 事项 | 我的建议 |
|---|---|
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 个路由扫过一遍」更有价值。
🔴 因为写操作才是能被利用的,读操作最多是信息泄露。
分层比全量更省力,也更抓重点。