- 三份 gateway 审计(20260801/0807/0809)合并为单一 docs/security/gateway_SECURITY_AUDIT.md - 已完成修复已合入 gateway/src/main.cpp 注释,认证相关项标注迁 ca - 新建 docs/security/ca_SECURITY_AUDIT.md,同步迁入 P2-3/P1-7/A2 等开放项 - 更新 docs/INDEX.md 审计表、ca/README.md 引用、auth_service_plan/libhttp 文档链接 Co-Authored-By: WorkBuddy <workbuddy@tencent.com>
21 KiB
认证独立化(CA 模块)开发文档
日期: 2026-08-09(v2 重写)
背景: gateway v0.8.0 单文件 main.cpp(2470 行),认证逻辑(约 65%)与反向代理耦合;core/dnfs2 通过 HTTP 回调 gateway 验签,gateway 既是流量入口又是认证权威,形成环形依赖
范围: ca(新模块)、gateway(瘦身为纯代理);WAF / core / dnfs2 / static / 前端零改动
前版: 2026-08-08 初版计划书(auth 双栈方案)。本版按已选定拓扑重写:数据流 waf → ca → gateway,gateway 不再验签。原双栈/后置方案见 §2.2 决策记录
0. 摘要
新建 CA(Central Auth)模块:C++20 服务,接管 gateway 当前监听的 127.0.0.1:8443(WAF upstream 不变),承载全部认证面(登录/注册/Token 生命周期/CAPTCHA 代理/限流/WS 代理),并作为认证前置层:对业务请求本地验签后注入可信身份头,转发给退居 127.0.0.1:8445 的 gateway。gateway 删除全部认证代码,不再验签,仅保留白名单反代。
核心收益:验签在 CA 本地完成(RSA 密钥同进程,零新增网络跳);core/dnfs2 仍调 8443 的 /admin/verify、/whoami,无感知切换;前端 URL 不变;现有黑盒 34 断言原样充当验收门。
1. 现状分析
1.1 当前调用链
客户端 ──HTTPS──▶ WAF:443 ──HTTP──▶ gateway:8443 ──HTTP──▶ static:9200 / dnfs2:8006 / core:5200
│ ├─ 本地验签(RSA 内存密钥)+ Cookie 回退 + Authorization 注入
│ ├─ /login /register /logout /refresh_token /whoami /admin/verify
│ ├─ /register/send_code /reset/send_code /reset_password
│ ├─ GET /captcha/image(pymsg 代理 captcha2:8009)
│ └─ WS 代理 /ws/aibox(握手验签 is_admin)
└── pymsg: db:8005 / captcha2:8009 / smtp3:8008 / confs:8010
下游验签回调(HTTP 回环):
core:5200 ──HTTP──▶ gateway:8443/admin/verify (HttpTask.cpp,GATEWAY_HOST/GATEWAY_PORT 可配,fail-closed)
dnfs2:8006 ──HTTP──▶ gateway:8443/whoami (gateway_url 配置项,fail-closed)
1.2 gateway 职责盘点与归属
| 职责域 | 函数/组件 | 归属 |
|---|---|---|
| 用户认证 | handleLogin / handleRegister / handleLogout / handleResetPassword / handleSendEmailCode / handleResetSendCode |
→ CA |
| Token 管理 | verify_token / generate_jti / revoke_jti / revoke_all_user_tokens / handleRefreshToken / handleWhoami / handleAdminVerify / jti 黑名单 / 用户版本号表 / RSA 密钥对 |
→ CA |
| 密码体系 | PBKDF2Hasher / PasswordValidator / 防枚举 dummy 校验 |
→ CA |
| 验证码/邮箱 | handleCaptchaImage / verify_captcha_remote / verify_email_code_remote |
→ CA |
| 速率限制 | 全部 RateLimiter 实例(登录 5/5min、注册 3/10min、发码 5/10min + 邮箱级 3/h+10/d、验证码 10/min 等) | → CA |
| WS 代理 | verifyWsAdminToken + _WS_PROXY_ROUTES 注册循环 |
→ CA(验签+桥接一体) |
| Cookie | q4_token 签发/清除、extract_token_from_request(Cookie 回退)、代理时 Authorization: Bearer 注入 |
→ CA |
| HTTP 反代 | handleProxy(白名单/路径安全/头转发/XFF) |
留 gateway(去验签化) |
| 头卫生 | is_special_header / is_safe_target |
留 gateway |
| 基础设施 | wrapperHttp / ConfIni / DbC / LogC / 信号处理 / 优雅关闭 |
两侧各持一份(模板见 smtp3) |
1.3 核心问题
- 职责耦合:认证与代理同进程,任一域改动爆炸半径为全文件
- 环形依赖:gateway 是流量入口又是验签权威,core/dnfs2 须 HTTP 回调入口
- 敏感面混杂:RSA 私钥、密码哈希校验、jti 黑名单与转发逻辑共用内存空间
- 不可独立扩展:认证密集与代理密集负载无法分别扩容;认证状态纯内存,绑定单实例
2. 目标架构
2.1 拓扑
客户端 ──HTTPS──▶ WAF:443 ──HTTP──▶ CA:8443 ──HTTP──▶ gateway:8445 ──▶ static:9200 / dnfs2:8006 / core:5200
│
├─ 本地处理:/login /register /logout /refresh_token /whoami /admin/verify
│ /register/send_code /reset/send_code /reset_password
│ GET /captcha/image
├─ WS:/ws/aibox 握手验签(is_admin) → 直接桥接 ws://127.0.0.1:8765
├─ 其余路径:本地验签 → 注入 X-Auth-User/Authorization → 转发 gateway:8445
└─ pymsg: db:8005 / captcha2:8009 / smtp3:8008 / confs:8010
端口安排:CA 接管 8443(WAF upstream 配置不变);gateway 迁 8445(仅 loopback,仅 CA 可达)。
职责划分:CA = 认证权威 + 认证前置代理;gateway = 纯路由/转发策略(opened 白名单、路径安全、XFF 透传)。
2.2 决策记录
| 方案 | 拓扑 | 结论 |
|---|---|---|
| 选定:CA 前置 | waf → ca → gateway |
✅ 验签在 CA 本地(零网络跳);core/dnfs2 调 8443 不变;WAF/前端不变;gateway 无需新增路径路由机制 |
| 否决 A:CA 后置双栈 | waf → gateway → ca(HTTP+pymsg 双栈) |
gateway 需新增路径级路由表;CORS/Set-Cookie/错误映射下沉 CA;两服务改动面都大 |
| 否决 B:CA 后置纯 pymsg | waf → gateway →(pymsg) ca |
gateway 保留 HTTP↔pymsg 翻译层与 Cookie 职责,认证面仍在入口进程内,瘦身不彻底;且每请求多一跳 pymsg 验签或引入吊销延迟的缓存 |
| 否决 C:验签缓存 | gateway 缓存验签结果 60s | logout/改密后 Token 在窗口内仍可用,吊销语义弱化(随拓扑选定自然消失) |
2.3 信任模型(重要)
- loopback 信任基线不变:本架构 loopback 内部服务本就不互鉴权(db:8005 execute 无认证即先例)。gateway:8445 仅绑
127.0.0.1,与本基线一致。 - 身份头契约(strip-then-set):CA 在边缘剥离客户端伪造的
X-Auth-User/X-Auth-Admin(Beast fields 大小写不敏感,erase后set),验签通过才注入。模式 A 下 CA 的唯一上游是 WAF(loopback)。 - gateway 存在性不变量:显式
Target-Url请求必须携带X-Auth-User,否则 401(非验签,仅防线编程,防本地误直连/SSRF 兜底)。 - 模式 B 警示迁移:CA 直连公网(无 WAF)时 XFF 可被伪造、限流可绕过——原 gateway README 的 A4 降级提示随限流一并迁入 CA README;根治方案 libhttp_remote_addr_dev_doc.md 中的
trust_xff/rate_limit_key()配套改在 CA 落地。
3. CA 模块设计
3.1 目录与构建(沿用 smtp3/captcha2 约定)
ca/
├── src/main.cpp # 入口 + HTTP 路由 + 认证逻辑 + 前置转发(迁移代码为主)
├── inc/
│ ├── Conf.hpp # ConfIni(confs 拉取 + env 覆盖 + 完整性校验)
│ ├── DbC.hpp # 自 gateway 随迁(已基于 libmsg us/Msg.hpp;client 名 "gateway"→"ca")
│ ├── LogC.hpp # 异步日志(smtp3 长连接版)
│ ├── ExpBase.hpp # 异常基类
│ └── LocalTime.hpp # 时间工具
├── Makefile # 链接 us_http / us_msg / us_crypt / ssl / crypto / boost_json / curl
├── _set.sh / _run.sh
└── README.md
- 二进制
ca.bin;版本从1.0.0起;日志服务名ca - 依赖:libhttp(HttpServer/HttpClient/ws_proxy)、libmsg(MsgClient/RpcClient)、libcrypt(MyRSA/Base64)、OpenSSL(PBKDF2/RAND_bytes)、boost::json/program_options/property_tree、libcurl
- 不依赖 libsqlite3:Users 表访问全部经 db:8005(现状即如此)
3.2 配置(confs/configs/ca.ini 新增)
[Server]
address = 127.0.0.1
port = 8443 # 接管 gateway 现端口,WAF upstream 不变
[Upstream]
gateway = http://127.0.0.1:8445 # 业务请求唯一上游
[Token] # 自 gateway.ini 原样迁入
keep_time = 3600
refresh_grace_multiplier = 2
max_refresh = 10
[Admin] # 原样迁入
users = 1776177662@qq.com;277665039@qq.com;
[WsProxy] # 自 gateway.ini 迁入
routes = /ws/aibox=ws://127.0.0.1:8765;
[CORS] # 原样迁入(P2-3:预检反射由 libhttp 处理,白名单暂仍不生效)
allowed_origins = https://qsont.me;https://www.qsont.me;http://127.0.0.1:8443;http://localhost:8443
[Downstream] # 原样迁入
db_host = 127.0.0.1
db_port = 8005
captcha_host = 127.0.0.1
captcha_port = 8009
smtp_host = 127.0.0.1
smtp_port = 8008
环境变量覆盖(与 gateway 现机制一致):LISTEN_ADDR/LISTEN_PORT、UPSTREAM_GATEWAY、DB_*、CAPTCHA_*、SMTP_*、CONFS_HOST/CONFS_PORT。配置加载:confs get 拉取(retry_on_refused 阻塞)→ env 覆盖 → 完整性校验。
3.3 HTTP 路由表(对外契约与原 gateway 完全一致)
| 路由 | 处理 | 说明 |
|---|---|---|
POST /login |
本地 | 强制远端 CAPTCHA;dummy PBKDF2 防枚举;签发 Token + q4_token Cookie |
POST /register |
本地 | 密码强度→邮箱格式→CAPTCHA→邮箱验证码(smtp fail-closed)→写 Users |
POST /register/send_code、/reset/send_code |
本地 | IP+邮箱级限流、强制 CAPTCHA、smtp 60s 冷却 429 透传 |
POST /reset_password |
本地 | 防枚举恒成功文案;改密后版本号递增全设备吊销 |
POST /logout |
本地 | jti 吊销 + 清 Cookie |
POST /refresh_token |
本地 | 宽限期刷新、旧 jti 吊销、max_refresh 上限 |
POST /whoami |
本地 | 恒 200,{login:false} 不泄露细节;core/dnfs2 依赖此契约 |
POST /admin/verify |
本地 | fail-closed 401;core 依赖此契约 |
GET /captcha/image |
本地 | pymsg 调 captcha2,PNG + X-Captcha-Token + no-store,10/min/IP |
WS [WsProxy] 路径 |
本地 | 握手验签 is_admin=true → 桥接后端(ws/wss),双向透传 |
GET/POST *(通配) |
转发 | 见 §3.4 |
3.4 前置转发层(通配路由 → gateway:8445)
迁移 handleProxy 的转发骨架,认证语义前置:
- 路径安全:
is_safe_target()校验(../%2e/%2f/%5c/空字节 → 400),CA/gateway 双层各执行一次(防线叠加,gateway 侧保留原校验)。 - 公开 vs 受保护:无
Target-Url头 → 公开目标(Proxy.default 语义在 gateway),剥离客户端X-Auth-User/X-Auth-Admin后直接转发,不注入任何凭证。 - 受保护:有
Target-Url→ 提取 Token(Authorization优先,回退q4_tokenCookie)→verify_token本地验签(RSA→过期→jti→版本号)→ 失败按现契约 400/401,不转发;成功则注入:Authorization: Bearer <token>(Cookie 来源也统一补齐,dnfs2 等下游零改动)X-Auth-User: <email>、X-Auth-Admin: true(仅管理员;供 gateway 不变量与下游未来免回调使用)
- 头卫生:剥离客户端自带的
X-Auth-User/X-Auth-Admin(strip-then-set);XFF 透传 WAF 值(缺省unknown);Host/connection/Accept-Encoding等逐跳头处理沿用现is_special_header集合 +x-auth-user/x-auth-admin新增。 - 转发失败语义:gateway:8445 不可达 → 502(与现 captcha/smtp fail-closed 风格一致),记 warning 日志。
- 响应:状态码与响应头原样回传(现
handleProxy行为),Connection: close。
3.5 迁移资产清单(gateway main.cpp → CA main.cpp)
代码以整体搬迁为主,保持实现逐字不变(降低回归面),仅做两处适配:日志服务名 gateway→ca;DbC client 名同上。
| 资产 | 备注 |
|---|---|
| PBKDF2Hasher / PasswordValidator / 常数时间比较 | 含 SHA256 旧哈希迁移注释块(保持注释状态) |
| TokenInfo / verify_token / generate_jti / revoke_jti / revoke_all_user_tokens / jti 黑名单(5000 上限清理)/ 用户版本号表 | RSA 密钥对启动内存生成(重启即全吊销,语义不变) |
| 8 个 RateLimiter 实例 + 邮箱级双窗口限流 | 限流键仍取 XFF(模式 A 可信;模式 B 警示见 §2.3-4) |
| handleLogin ~ handleResetPassword 全部 handler | 错误文案/状态码契约逐字保留(黑盒断言依赖) |
| handleWhoami / handleAdminVerify / handleRefreshToken / handleLogout | 同上 |
| handleCaptchaImage / verify_captcha_remote / verify_email_code_remote | fail-closed 语义保留 |
| verifyWsAdminToken + ws_proxy 注册 | 桥接目标直接为后端(不再经 gateway) |
| handleProxy 转发骨架 / is_safe_target / is_special_header / get_cookie_value / extract_token_from_request | 按 §3.4 改造 |
| wrapperHttp / 信号处理 / 优雅关闭(8s+3s 超时)/ ExpBase / ConfIni / LocalTime / DbC / LogC | 基础设施随迁 |
3.6 不变量自检(启动时)
[Upstream] gateway非空且为http://127.0.0.1:*(防误配到公网)- 模式 B 检测:
Server.address非 loopback 时启动日志打印 A4 安全降级警告
4. gateway 瘦身设计(v0.9.0)
4.1 删除清单(预计 -1700 行,2470 → ~750 行)
- 全部认证 handler 与 Token/PBKDF2/RSA/jti/版本号/限流/CAPTCHA/邮箱验证码代码
q4_tokenCookie 读写、extract_token_from_request、代理内验签与 Authorization 注入[Token]/[Admin]/[WsProxy]/[Database]配置读取;下游 captcha/smtp 配置(db 保留供 LogC)/login等 9 条认证路由 +/captcha/image路由 + WS 代理注册循环
4.2 保留清单
handleProxy(去验签化)+is_safe_target+is_special_header+wrapperHttp- 存在性不变量:显式
Target-Url且无X-Auth-User→ 401(§2.3-3) - 公开目标规则:无
Target-Url→Proxy.default,不校验身份头 - XFF 处理:透传 CA 值(缺省
unknown) - 配置加载、信号处理、优雅关闭、LogC
4.3 精简后 confs/configs/gateway.ini
[Server]
address = 127.0.0.1
port = 8445 # 迁出 8443;仅 CA 可达
tls = off
[Proxy]
default = http://127.0.0.1:9200
closed = http://127.0.0.1:8005;http://127.0.0.1:8007;
opened = http://127.0.0.1:9200;http://127.0.0.1:8006;http://127.0.0.1:5200;
[Downstream] # 仅供 LogC 日志落库
db_host = 127.0.0.1
db_port = 8005
4.4 启动自检
Server.address必须为 loopback(127.0.0.1/::1):gateway 不再验签后,绑定公网等于裸奔,非 loopback 直接拒绝启动并打印致命错误。
5. 接口契约
5.1 对外 HTTP(不变量)
https://<host>/login、/register、/whoami 等全部 URL、请求/响应 JSON 结构、状态码(400/401/403/429/500/502)、q4_token Cookie 属性、X-Captcha-Token 头——逐字不变。前端、WAF、core、dnfs2 零改动的依据即此。
5.2 内部头契约(CA ↔ gateway ↔ 下游)
| 头 | 方向 | 语义 |
|---|---|---|
X-Auth-User |
CA→gateway→下游 | 已验签身份;CA strip-then-set;gateway 对显式 Target-Url 要求其存在 |
X-Auth-Admin |
CA→gateway→下游 | true 表示管理员;供下游免回调豁免(可选消费) |
Authorization |
CA→gateway→下游 | Cookie 来源统一补齐为 Bearer;下游解析不变 |
X-Forwarded-For |
WAF→CA→gateway→下游 | 逐层透传,缺省 unknown |
Target-Url |
客户端→CA→gateway | 路由策略头,语义不变 |
5.3 Token 格式(不变)
Base64(payload)_RSA-Sign(payload);payload 字段(user/jti/ver/iat/created_at/keep_time/is_admin/refresh_count/max_refresh)不变。切换瞬间旧 Token 全部失效(CA 新密钥),用户重新登录——一次性事件,cutover 通告中说明。
5.4 下游零改动验证点
| 消费方 | 现状调用 | 切换后 |
|---|---|---|
core verify_admin_token |
http://127.0.0.1:8443/admin/verify |
同 URL 由 CA 服务,契约不变 |
| dnfs2 Token 验签 | http://127.0.0.1:8443/whoami |
同上 |
static /api/check_user |
直连 db 查 Users(账号旁路) | 不受影响;遗留项另行收口 |
6. 上线步骤(cutover 与回滚)
| 步骤 | 内容 |
|---|---|
| 1 | CA 开发完成,LISTEN_PORT=18443 并行实例联调(不影响现网 8443) |
| 2 | gateway 瘦身版(v0.9.0)构建,gateway.ini 改 8445 |
| 3 | cutover(秒级窗口):停 gateway:8443 → 启 CA:8443 → 启 gateway:8445 → 黑盒全量验证 |
| 4 | core/init.txt 增加 ca/_run.sh(gateway 之前);README/INDEX 文档同步 |
| 5 | 观察 24h:CA/gateway 日志、db Logs 表 4xx/5xx 基线对比 |
回滚:停 CA → 旧 gateway 二进制 + 旧配置回 8443 → 重启。Token 全部重签(用户重新登录),无数据迁移。
7. 测试计划
7.1 验收门(零改动复用)
tests/blackbox/test_gateway.py34 断言原样对 8443 执行:认证路径打到 CA、代理路径经 CA→gateway,外部契约不变则全绿(当前基线 32 passed / 2 env-skipped)- 修复期两个红测试(A3 缺字段 400)已在 v0.8.0 转绿,迁移后必须保持
7.2 新增探针(test_ca.py,黑盒补充)
| 探针 | 预期 |
|---|---|
客户端伪造 X-Auth-User: admin@x + 显式 Target-Url(无 Token) |
CA 剥离后 401,不到下游 |
伪造 X-Auth-User + 有效非管理员 Token 访问 WS 路由 |
403(以验签结果为准,不以头为准) |
直连 gateway:8445,显式 Target-Url、无 X-Auth-User |
401(存在性不变量) |
| 经 CA 全链路(8443)带 Token 访问 dnfs2 | 下游收到 X-Auth-User 与 Authorization |
| CA 存活、gateway:8445 停止 | 业务路径 502(不静默放行),认证路径正常 |
| gateway 存活、CA 停止 | WAF 502(入口不可达,符合预期 fail-closed) |
| 公开目标(无 Target-Url)带伪造身份头 | 下游收不到 X-Auth-User/Authorization |
7.3 集成回归
- core HTTP 管理接口(
/admin/verify链路)实测;dnfs2 笔记 CRUD(/whoami链路 + AI 审核)实测 - WS 代理端到端:管理员 Token 升级
/ws/aibox双向帧;非管理员 403 - 限流全谱在 CA 复测(随机 XFF 技巧沿用);cutover/回滚演练各一次
7.4 测试资产演进(后续)
test_gateway.py按进程边界拆分为test_ca.py(A/B/C/E 段)+ 精简test_gateway.py(D 段代理矩阵 + 8445 直连探针),README/INDEX 同步- 认证 happy path 仍依赖 captcha2/smtp 测试钩子(既有遗留,CA 落地后可仅在 CA 层加
[Security] captcha_required开关,单点解锁)
8. 风险与缓解
| 风险 | 等级 | 缓解 |
|---|---|---|
| CA 不可用 → 全站入口中断 | 高 | CA 职责内聚后可用性等同于现 gateway;core 守护 + 黑盒监控;回滚方案秒级 |
| 迁移遗漏导致认证行为漂移 | 中 | 代码逐字搬迁 + 34 断言验收门 + §7.2 探针矩阵 |
| 身份头伪造/泄露 | 中 | strip-then-set + gateway 存在性不变量 + 公开目标不注入 + 直连探针回归 |
| gateway:8445 被公网暴露 | 高 | 启动强制 loopback 自检(§4.4);部署审计 checklist 增加端口扫描项 |
| cutover 窗口 Token 全失效 | 低 | 预期内(RSA 换钥),通告用户重新登录 |
| 双重转发延迟 | 低 | 本机 HTTP 转发 <1ms;CA→gateway 短连接可后续优化为 keep-alive |
| 模式 B 限流绕过(A4 迁移) | 中 | CA README 降级警示;根治按 libhttp_remote_addr_dev_doc(配套改落 CA) |
9. 工作量估算
| 部分 | 规模 | 估时 |
|---|---|---|
| CA 脚手架(目录/Makefile/_set.sh/_run.sh/LogC 等) | 模板复制 + 适配 | 0.5 天 |
| 认证资产搬迁 + 前置转发层改造 | ~1700 行搬迁 + ~150 行改造 | 1.5 天 |
| gateway 瘦身 + 启动自检 | -1700 行 + ~20 行 | 0.5 天 |
| 配置/init.txt/文档(README×2、INDEX、confs) | — | 0.5 天 |
| 黑盒验收 + §7.2 探针 + 集成回归 + cutover 演练 | — | 1 天 |
| 合计 | 4 天(含缓冲) |
10. 与既有文档的关系
- 本文档替代 2026-08-08 初版计划书中的双栈拓扑与阶段划分(初版的职责盘点/迁移清单仍然有效,已并入 §1.2/§3.5)
- libhttp_remote_addr_dev_doc.md(A4 根治):实施时
trust_xff/rate_limit_key()配套由 gateway 改为落 CA;is_special_header两侧同步加x-remote-addr - gateway_SECURITY_AUDIT.md:A2(白名单 403)断言、P2-3(CORS 白名单)随转发层/配置迁入 CA;P1-7(CSRF Token)在 CA 层评估
- gateway README / 项目 README 的架构图、端口表、请求链路在上线步骤第 4 步同步更新