# captcha 模块安全审计报告 **审计日期**: 2026-08-01 **审计范围**: `captcha_service.py`、`log_client.py` **代码版本**: dev_qing 分支 --- ## 代码验证记录 **验证日期**: 2026-08-03 **验证方式**: 逐条对照审计报告与当前代码(`captcha_service.py`、`log_client.py`、`_run.sh`) ### 验证结果汇总 | 等级 | 总数 | 已修复 | 部分修复 | 未修复 | 修复率 | |------|------|--------|----------|--------|--------| | P0 | 0 | 0 | 0 | 0 | - | | P1 | 1 | 0 | 0 | 1 | 0% | | P2 | 1 | 0 | 0 | 1 | 0% | | **合计** | **2** | **0** | **0** | **2** | **0%** | ### 逐条验证 | 编号 | 状态 | 代码位置 | 验证说明 | |------|------|----------|----------| | P1-1 | ❌ 未修复 | `captcha_service.py` L128-129, L204-222 | `captcha_store` dict 无容量上限,`_handle_image` 无生成速率限制,大量请求可耗尽内存 | | P2-1 | ❌ 未修复 | `captcha_service.py` L128 | 仍为进程内存 dict,未迁移至 Redis;当前单实例可接受 | --- ## 修复进度 | 优先级 | 总数 | 已修复 | 待修复 | |--------|------|--------|--------| | P0 | 0 | 0 | 0 | | P1 | 1 | 0 | 1 | | P2 | 1 | 0 | 1 | --- ## 🟠 P1 — 高危问题 | # | 问题 | 说明 | |---|------|------| | P1-1 | 验证码存储无容量限制,大量生成可耗尽内存 | 无验证码数量上限,攻击者可高频调用 `/captcha/image` 生成大量验证码填满内存 | --- ## 🟡 P2 — 中危问题 | # | 问题 | |---|------| | P2-1 | 内存存储不支持多实例水平扩展(需换 Redis),当前单实例可接受 | --- ## ✅ 已有安全机制 1. ✅ **一次性使用**: verify 后立即 `pop` 删除 Token(`captcha_service.py` L231) 2. ✅ **5 分钟过期**: 惰性清理(`captcha_service.py` L213-215) 3. ✅ **常数时间比较**: XOR 防时序攻击(`captcha_service.py` L239-245) 4. ✅ **仅监听 127.0.0.1**: 不对外暴露(`captcha_service.py` L181) 5. ✅ **gateway 层代理**: `Cache-Control: no-store` 禁止缓存验证码图片 6. ✅ **依赖 pymsg 协议**: recv_timeout 防 Slowloris 7. ✅ **图形验证码使用 PIL 生成**: 不依赖外部服务 8. ✅ **两层配置加载**: confs 拉取 `captcha.ini` + 环境变量覆盖(`captcha_service.py` L46-113) 9. ✅ **配置白名单合并**: `_merge_ini` 仅覆盖已知 section/key,防止意外字段注入(`captcha_service.py` L51-65) 10. ✅ **配置完整性校验**: 缺失必填项时启动失败(`captcha_service.py` L103-112) 11. ✅ **confs retry_on_refused**: confs 未就绪时自动阻塞重试(`captcha_service.py` L76-77) 12. ✅ **Token 使用 secrets.token_hex**: 加密安全随机(`captcha_service.py` L207) 13. ✅ **log_client 异步批量 + 队列上限**: `QUEUE_MAXSIZE=10000`,队列满时丢弃最旧条目(`log_client.py` L38) 14. ✅ **log_client 异常安全网**: `_on_error` 捕获 handler 外异常,线程不崩溃(`log_client.py` L150-153) --- ## 代码审查发现 ### NEW-1: 验证码答案使用 random 模块生成(P2) - **位置**: `captcha_service.py` L134-135 - **问题**: `generate_text()` 使用 `random.choices` 而非 `secrets` 模块生成验证码答案。`random` 模块是确定性伪随机,若攻击者能推测随机种子状态,可预测验证码答案 - **影响**: 低(Token 使用 `secrets.token_hex` 保护,攻击者需先获取有效 Token) - **建议**: 将 `random.choices` 替换为 `secrets.SystemRandom().choices` 或逐个调用 `secrets.choice` ### NEW-2: 验证码图片生成无速率限制(P1-1 补充) - **位置**: `captcha_service.py` L204-222 - **问题**: `_handle_image` 无任何速率限制,且 PIL 图片生成是 CPU 密集型操作,高频调用不仅消耗内存,还消耗 CPU - **建议**: 添加滑动窗口限流(如单 IP 每分钟最多 10 次),与 smtp 模块对齐 --- ## 修复路线图 ### 短期 - [ ] 添加验证码存储容量上限(如最多 10000 个,超出时清理最早的) - [ ] 添加生成速率限制(单 IP 每分钟最多生成 N 个) - [ ] 将 `generate_text()` 中的 `random.choices` 替换为 `secrets` 模块 ### 中期 - [ ] 考虑替换为 Redis 存储支持水平扩展(当前单实例足够)