2026-09-22 18:53:53 +08:00
|
|
|
|
# passport(用户账号与身份认证)代码审计报告
|
|
|
|
|
|
|
|
|
|
|
|
| 项 | 内容 |
|
|
|
|
|
|
| --- | --- |
|
|
|
|
|
|
| 审计对象 | `module/base/passport` |
|
|
|
|
|
|
| 服务域 | 基础与平台服务 |
|
|
|
|
|
|
| 审计日期 | 2026-09-22 |
|
|
|
|
|
|
| 代码规模 | 手写 Go 文件约 44 个(含 `cmd/`、`internal/`、`service/`、`proto/` 6 个);`pb/` 下约 21 个生成 `.go` 仅看签名 |
|
|
|
|
|
|
| 入口 | `cmd/main`(gRPC + grpc-gateway 单进程)、`cmd/cli`(调试入口)、聚合入口 `pkgs/all`、`pkgs/ecmall` |
|
|
|
|
|
|
| 对外协议 | gRPC(`passport.Account/Login/Register/Forget/Verify`)+ REST(grpc-gateway,路径形如 `POST /passport.Login/Pwd`) |
|
|
|
|
|
|
| 结论摘要 | 认证核心存在**致命缺陷**:验证码登录/注册/找回均**不校验验证码**;找回密码仅凭 `identity` 即可改密且以**未加盐 MD5** 存储;密码哈希使用 `bcrypt.MinCost`(cost=4);KYC 回调为**空 stub**;Token 缓存键前缀大小写不一致。 |
|
|
|
|
|
|
|
|
|
|
|
|
## 1. 服务定位与职责
|
|
|
|
|
|
|
|
|
|
|
|
用户通行证(账号、密码、第三方登录、KYC 认证、标签、统计)的统一身份服务。负责账号注册、密码/验证码登录、密码找回、KYC 验证回调、用户资料与标签管理。它是**认证与账户存储**服务,不负责网关鉴权本身(鉴权依赖上层网关注入 metadata,由 `service.ParseMetaCtx` 读取)。
|
|
|
|
|
|
|
|
|
|
|
|
## 2. 代码结构与入口
|
|
|
|
|
|
|
|
|
|
|
|
| 路径 | 职责 |
|
|
|
|
|
|
| --- | --- |
|
|
|
|
|
|
| `cmd/main/main.go` | 独立进程入口:配置→初始化 impl→建 gRPC/Gateway→`server.New(nil)`→`service.New` 启动 |
|
|
|
|
|
|
| `cmd/cli/main.go` | 调试入口:仅打印数据库配置 |
|
|
|
|
|
|
| `internal/config/config.go` | 配置结构(`Base/Databases/MicroService/Gateway/Token/Kyc/WeChat` 等)与校验;设置 JWT 过期 |
|
|
|
|
|
|
| `internal/impl/impl.go` | 初始化 Redis、DB、Etcd、内存缓存实例 |
|
|
|
|
|
|
| `internal/vars/{token,status,provider}.go` | 常量:`TokenPrefix="/TOKEN/"`、账号状态、第三方 provider |
|
|
|
|
|
|
| `internal/excode/ex.go` | **空文件**,无内容 |
|
|
|
|
|
|
| `internal/models/*` | 11 个模型 + `query.go`(InitData/Exists) + `cache.go`(Redis 缓存与验证码接口) |
|
|
|
|
|
|
| `internal/logic/common/token.go` | 用 `env.Runtime.JwtSecretKey` 签发 JWT |
|
|
|
|
|
|
| `internal/logic/{login,register,forget,account,verify}/*` | 各业务方法实现 |
|
|
|
|
|
|
| `internal/server/*` | gRPC server 注册(5 个 service 全部委托 logic) |
|
|
|
|
|
|
| `service/{expose,dependencies}.go` | 聚合宿主注入接口(供 `pkgs/all` 复用共享连接) |
|
|
|
|
|
|
| `proto/*.proto` | 5 个服务契约(**无 `google.api.http` 注解**,REST 路径由网关生成) |
|
|
|
|
|
|
|
|
|
|
|
|
依赖注入:`service.Dependencies` 支持外部传入 Redis/Etcd/DB/Cache,非 nil 时覆盖 `internal/impl` 对象,供聚合入口复用共享连接。
|
|
|
|
|
|
|
|
|
|
|
|
## 3. 接口清单
|
|
|
|
|
|
|
|
|
|
|
|
> proto 中未声明 `ping/hello` 服务,但 `etc/*.yaml` 的 `MicroService.Anonymous` 列出 `passport.ping.hello`,与当前模块实现不一致(疑似残留)。
|
|
|
|
|
|
|
|
|
|
|
|
| 方法 | 路径(gRPC / REST) | 功能 | 鉴权 | 实现位置 |
|
|
|
|
|
|
| --- | --- | --- | --- | --- |
|
|
|
|
|
|
| `Login.Pwd` | `passport.Login/Pwd` | 账号+密码登录 | 否(公开) | `logic/login/pwd.go` |
|
|
|
|
|
|
| `Login.Code` | `passport.Login/Code` | 验证码登录 | 否(公开) | `logic/login/code.go` |
|
|
|
|
|
|
| `Login.Quick` | `passport.Login/Quick` | 验证码快捷登录并注册 | 否(公开) | `logic/login/quick.go` |
|
|
|
|
|
|
| `Register.Pwd` | `passport.Register/Pwd` | 账号密码注册 | 否(公开) | `logic/register/{pwd,do}.go` |
|
|
|
|
|
|
| `Register.Code` | `passport.Register/Code` | 手机验证码注册 | 否(公开) | `logic/register/{code,do}.go` |
|
|
|
|
|
|
| `Forget.Verify` | `passport.Forget/Verify` | 验证手机号和验证码 | 否(公开) | `logic/forget/verify.go` |
|
|
|
|
|
|
| `Forget.Reset` | `passport.Forget/Reset` | 重置密码 | 否(公开) | `logic/forget/reset.go` |
|
|
|
|
|
|
| `Account.Get` | `passport.Account/Get` | 获取完整用户信息 | 是(`ParseMetaCtx`) | `logic/account/get.go` |
|
|
|
|
|
|
| `Account.SetData` | `passport.Account/SetData` | 更新用户资料 | 是 | `logic/account/set_data.go` |
|
|
|
|
|
|
| `Account.SetPassword` | `passport.Account/SetPassword` | 修改密码 | 是 | `logic/account/set_password.go` |
|
|
|
|
|
|
| `Account.TagCreate` | `passport.Account/TagCreate` | 新增标签 | 是 | `logic/account/tag_create.go` |
|
|
|
|
|
|
| `Account.TagRemove` | `passport.Account/TagRemove` | 删除标签 | 是 | `logic/account/tag_remove.go` |
|
|
|
|
|
|
| `Account.Statistics` | `passport.Account/Statistics` | 获取统计数据 | 是 | `logic/account/statistics.go` |
|
|
|
|
|
|
| `Verify.Request` | `passport.Verify/Request` | 发起 KYC 验证 | 是 | `logic/verify/request.go` |
|
|
|
|
|
|
| `Verify.JumioCallback` | `passport.Verify/JumioCallback` | KYC 回调 | 否(公开) | `logic/verify/jumio_callback.go` |
|
|
|
|
|
|
|
|
|
|
|
|
> 全部声明的 gRPC 方法均已委托到 logic(server 层无 Unimplemented 未实现项);但 `Verify.JumioCallback` 与 `Forget.Verify` 为**逻辑占位/伪实现**(见 6.3)。
|
|
|
|
|
|
|
|
|
|
|
|
## 4. 数据模型与表
|
|
|
|
|
|
|
|
|
|
|
|
| 表名 | 字段 | 类型 | 键/约束 | 说明 |
|
|
|
|
|
|
| --- | --- | --- | --- | --- |
|
|
|
|
|
|
| `passport_account` | `id` | uint | PK | 自增主键 |
|
|
|
|
|
|
| | `identity` | varchar | - | 用户唯一码(对外公开) |
|
|
|
|
|
|
| | `account` | varchar(255) | 默认 `''` | 账号 |
|
|
|
|
|
|
| | `phone` | varchar(20) | 默认 `''` | 手机号 |
|
|
|
|
|
|
| | `email` | varchar(255) | 默认 `''` | 邮箱 |
|
|
|
|
|
|
| | `password` | varchar(255) | not null | 密码(bcrypt;重置时误写为 MD5) |
|
|
|
|
|
|
| | `salt` | varchar(255) | not null | 密码盐 |
|
|
|
|
|
|
| `passport_data` | `passport_id`/`passport_identity` | - | - | 关联账号 |
|
|
|
|
|
|
| | `nickname/avatar/sign/cover` | varchar | 默认 `''` | 资料 |
|
|
|
|
|
|
| | `sex/birthday` | int8/time | - | 性别/生日 |
|
|
|
|
|
|
| | `country/province/city/area` | varchar | 默认 `''` | 地区 |
|
|
|
|
|
|
| | `score/level` | int32 | 默认 0 | 积分/等级 |
|
|
|
|
|
|
| | `rights` | varchar(255) | 默认 `''` | 权限 |
|
|
|
|
|
|
| | `email_verify/phone_verify/face_verify/document_verify/kyc_verify` | int32 | 默认 0 | 各验证状态 |
|
|
|
|
|
|
| `passport_tags` | `passport_id`/`name`/`icon` | - | - | 用户标签 |
|
|
|
|
|
|
| `passport_verify` | `passport_id` + 各验证时间/证件/KYC 字段 | - | - | 认证详情(KYC 回调未写入) |
|
|
|
|
|
|
| `passport_provider` | `provider`/`provider_id`/`access_token`/`refresh_token` | - | - | 第三方登录绑定 |
|
|
|
|
|
|
| `passport_score` / `passport_statistics` / `passport_rights_expiry` / `passport_notify` | 见各模型文件 | - | - | 积分/统计/特权有效期/消息 |
|
|
|
|
|
|
|
|
|
|
|
|
## 5. 核心流程
|
|
|
|
|
|
|
|
|
|
|
|
```mermaid
|
|
|
|
|
|
flowchart TD
|
|
|
|
|
|
A["POST passport.Login/Code"] --> B["校验 phone/code/country 非空"]
|
|
|
|
|
|
B --> C["GetPassportAccountByField(phone)"]
|
|
|
|
|
|
C --> D["直接签发 JWT 返回"]
|
|
|
|
|
|
D --> E["未比对 Redis 验证码"]
|
|
|
|
|
|
F["POST passport.Forget/Reset"] --> G["校验 identity/password 非空"]
|
|
|
|
|
|
G --> H["Update password = Md5(pwd)"]
|
|
|
|
|
|
H --> I["账号被接管/密码无法登录"]
|
|
|
|
|
|
J["POST passport.Login/Pwd"] --> K["bcrypt.CompareHashAndPassword"]
|
|
|
|
|
|
K --> L["签发 JWT 并写 Redis(/TOKEN/)"]
|
|
|
|
|
|
M["POST passport.Register/Pwd"] --> N["bcrypt.MinCost 哈希+盐"]
|
|
|
|
|
|
N --> O["Create account + data"]
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
## 6. 审计发现
|
|
|
|
|
|
|
|
|
|
|
|
### 6.1 安全
|
|
|
|
|
|
|
|
|
|
|
|
| 级别 | 位置 | 问题 |
|
|
|
|
|
|
| --- | --- | --- |
|
|
|
|
|
|
| **高** | `internal/logic/login/code.go:16-59` | **验证码登录不校验验证码**。仅判断 `in.Phone/in.Code/in.Country` 非空后直接 `GetPassportAccountByField("phone",...)` 并签发 JWT;从未调用 `GetVerificationCodeCache` 比对 `in.Code`。任意已知手机号 + 任意 code 即可登录。 |
|
|
|
|
|
|
| **高** | `internal/logic/login/quick.go:20-64` | **快捷登录并注册同样不校验 code**,且当手机号不存在时(:32-64)自动创建账号。攻击者可用任意 code 对任意手机号批量建号并登录。 |
|
|
|
|
|
|
| **高** | `internal/logic/forget/reset.go:17-41` | **找回密码无验证码/令牌校验**。仅凭 `identity` + `password` 即 `Update("password", ...)`。前置 `Forget.Verify` 也未真正校验 code(见 6.3)。`identity` 在登录/注册响应中公开返回,攻击者可据此直接接管任意账号。 |
|
|
|
|
|
|
| **高** | `internal/logic/forget/reset.go:31` | **以未加盐 MD5 存储密码**:`Update("password", utils.Md5(in.Password))`,与全模块 bcrypt 体系不一致;且登录校验走 bcrypt(`login/pwd.go:27`),重置后的密码既不安全又无法用于登录(逻辑自相矛盾)。 |
|
|
|
|
|
|
| 中 | `internal/logic/register/do.go:30`、`internal/logic/account/set_password.go:35`、`internal/models/query.go:21` | **密码哈希 cost 过低**:使用 `bcrypt.MinCost`(=4)而非默认 10,抗暴力破解能力弱。 |
|
|
|
|
|
|
| 中 | `internal/vars/token.go:3` vs `internal/logic/register/do.go:84` | **Token 缓存键前缀不一致**:`vars.TokenPrefix="/TOKEN/"` 用于 `login/pwd.go:48`、`login/code.go:41`;而 `register/do.go:84` 用 `config.Spec.Token.Prefix`(yaml 为 `/token/`)。同一 identity 在 Redis 中形成两把不同键,登出/校验可能取错或失效。 |
|
|
|
|
|
|
| 低 | `internal/logic/login/code.go:41` | 写 Redis token 时 TTL 传 `0`(永不过期),与 `login/pwd.go:48` 用 `_vars.JwtExpire` 不一致;缓存 token 永久留存(仅依赖 JWT 自身过期)。 |
|
|
|
|
|
|
| 低 | `test/grpc/main.go:45` | 硬编码测试 JWT(`authorization` 明文 token,含 `id/identity`)出现在测试代码,若被复用可越权。 |
|
|
|
|
|
|
|
|
|
|
|
|
### 6.2 正确性与逻辑缺陷
|
|
|
|
|
|
|
|
|
|
|
|
| 级别 | 位置 | 问题 |
|
|
|
|
|
|
| --- | --- | --- |
|
|
|
|
|
|
| 中 | `internal/logic/account/get.go:44` | `impl.DBService.Where("passport_id=?", AUTH.ID).First(data)` **未处理 error**;查询失败或记录不存在时 `data` 仍是零值指针(非 nil),`if data != nil` 恒真,后续 `data.Birthday.Format` 等执行,DB 错误被吞掉、返回空资料。 |
|
|
|
|
|
|
| 中 | `internal/logic/forget/reset.go:31` | 重置密码**不同步更新 `salt`**(`passport_account.salt` 列存在),且写入 MD5,与 bcrypt 校验路径不兼容(同 6.1)。 |
|
|
|
|
|
|
| 中 | `internal/logic/register/do.go:53,70` | 账号与扩展数据两次 `Create` **非事务**:若第二步失败,会留下无扩展数据的孤儿账号。 |
|
|
|
|
|
|
| 低 | `internal/logic/register/code.go:13-31` | `Code` 注册仅校验手机号格式与唯一性,**未校验 `in.Code`**,与"验证码注册"语义不符(同 6.1 高)。 |
|
|
|
|
|
|
|
|
|
|
|
|
### 6.3 未完成实现
|
|
|
|
|
|
|
|
|
|
|
|
- `internal/logic/verify/jumio_callback.go:13-35`:KYC 回调**仅 `printer.Info` 打印完整 payload 后返回成功**,未校验回调签名、未更新 `passport_verify` 的 KYC 状态——典型占位实现。
|
|
|
|
|
|
- `internal/logic/forget/verify.go:14-32`:名义"验证手机号和验证码",实际**不比对 code**(无任何 Redis 验证码查询),属伪实现。
|
|
|
|
|
|
- `internal/models/query.go:12-39`:`InitData` 计划初始化 `demo/welcome` 默认账号(密码 bcrypt MinCost),但 `cmd/main/main.go:37` 已注释未启用;含**硬编码默认口令**,属潜在隐患(一旦启用即成后门)。
|
|
|
|
|
|
- `internal/models/cache.go:144-172`:`IncrementLoginAttempts`/`ClearLoginAttempts` **从未被调用**,README 声称的"内置限流"未落地。
|
|
|
|
|
|
- `internal/models/cache.go:124-142`:`SetVerificationCodeCache`/`GetVerificationCodeCache` 提供验证码存取,但**所有登录/注册/找回逻辑均未调用**,验证码形同虚设(与 6.1 高关联)。
|
|
|
|
|
|
- `internal/excode/ex.go`:空文件,错误码未定义。
|
|
|
|
|
|
- `cmd/cli/main.go`:仅打印 DB 配置,无实际用途(调试残留)。
|
|
|
|
|
|
- `etc/*.yaml` 的 `SecretKey: CHANGE_ME` 字段在 `config.go` 的 `SrvConfig` **无对应字段**,未被解析;真实 JWT 密钥来自 `env.Runtime.JwtSecretKey`(`logic/common/token.go:10`),其初始化不在本模块可见范围【信息不足】。
|
|
|
|
|
|
- 全模块**无 `*_test.go` 单元测试**。
|
|
|
|
|
|
|
|
|
|
|
|
### 6.4 健壮性与可维护性
|
|
|
|
|
|
|
|
|
|
|
|
| 级别 | 位置 | 问题 |
|
|
|
|
|
|
| --- | --- | --- |
|
|
|
|
|
|
| 中 | `internal/logic/account/get.go:44` | DB 错误未处理(同 6.2)。 |
|
|
|
|
|
|
| 中 | `internal/logic/login/*`、`forget/reset.go`、`verify/jumio_callback.go` | 多处 `printer.Error/Info` 输出内部错误与敏感参数(如 `jumio_callback.go:21` 打印完整 KYC 回调含 PII、`reset.go:33` 打印 identity、`login/pwd.go:34` 打印 DB 错误),存在信息泄露。 |
|
|
|
|
|
|
| 低 | `internal/vars/token.go:3` vs `config` | Token 前缀两套定义(见 6.1)。 |
|
|
|
|
|
|
| 低 | 全模块 | 无单元测试;`InitData`/`CreateAccount` 等存在死代码或未被调用路径。 |
|
|
|
|
|
|
|
|
|
|
|
|
## 7. 风险汇总
|
|
|
|
|
|
|
|
|
|
|
|
| 编号 | 级别 | 问题 | 影响面 |
|
|
|
|
|
|
| --- | --- | --- | --- |
|
|
|
|
|
|
| P1 | 高 | 验证码登录不校验 code | 任意手机号可登录,认证失效 |
|
|
|
|
|
|
| P2 | 高 | 快捷登录/注册不校验 code 且自动建号 | 批量伪造账号 |
|
|
|
|
|
|
| P3 | 高 | 找回密码仅凭 identity 改密、无校验 | 任意账号被接管 |
|
|
|
|
|
|
| P4 | 高 | 重置密码以未加盐 MD5 存储且与 bcrypt 不兼容 | 密码泄露风险 + 重置后无法登录 |
|
|
|
|
|
|
| P5 | 中 | bcrypt.MinCost(cost=4) | 密码暴力破解成本降低 |
|
|
|
|
|
|
| P6 | 中 | Token 缓存键前缀大小写不一致 | 登录态缓存错乱/登出失效 |
|
|
|
|
|
|
| P7 | 中 | KYC 回调空 stub、Forget.Verify 伪实现 | 认证状态不可信 |
|
|
|
|
|
|
| P8 | 中 | 验证码缓存接口从未被调用 | 全链路验证码机制失效 |
|
|
|
|
|
|
| P9 | 低 | 内部错误/PII 经日志泄露 | 信息泄露 |
|
|
|
|
|
|
| P10 | 低 | DB 错误未处理、无事务、无测试 | 健壮性与可维护性 |
|
|
|
|
|
|
|
|
|
|
|
|
## 8. 修复建议(务实项)
|
|
|
|
|
|
|
|
|
|
|
|
1. **验证码必须校验**:在 `login/code.go`、`login/quick.go`、`register/code.go`、`forget/verify.go` 中调用 `GetVerificationCodeCache(phone)` 并比对 `in.Code`(一次一消);`forget/reset.go` 必须要求先通过 `Verify` 且校验一次性令牌,再允许改密。
|
|
|
|
|
|
2. **重置密码用 bcrypt**:`forget/reset.go:31` 改为 `bcrypt.GenerateFromPassword([]byte(newPwd+salt), bcrypt.DefaultCost)` 并同步更新 `salt`,与登录校验一致。
|
|
|
|
|
|
3. **提高哈希 cost**:`register/do.go`、`account/set_password.go`、`query.go` 的 `bcrypt.MinCost` 改为 `bcrypt.DefaultCost`(10)。
|
|
|
|
|
|
4. **统一 Token 前缀**:仅保留一处定义(建议用 `config.Spec.Token.Prefix`),`login` 与 `register` 共用,避免 `/TOKEN/` 与 `/token/` 双键。
|
|
|
|
|
|
5. **KYC 回调落地**:`jumio_callback.go` 增加签名校验并更新 `passport_verify` 的 `kyc_verify`/`kyc_status`;`Forget.Verify` 真正比对验证码。
|
|
|
|
|
|
6. **收敛默认账号**:保持 `InitData` 不启用,或删除该函数与硬编码 `welcome` 口令。
|
|
|
|
|
|
7. **错误处理**:`account/get.go:44` 处理 `First` 的 error;`register/do.go` 两次 Create 用事务包裹。
|
|
|
|
|
|
8. **清理**:移除 `cmd/cli` 调试输出、空 `excode/ex.go`、未调用的限流函数(或真正接入);敏感信息勿进 `printer`。
|
|
|
|
|
|
|
|
|
|
|
|
> 本报告只列出与现有实现直接相关的修复项,不引入新的分层或抽象封装。
|
2026-09-22 21:15:34 +08:00
|
|
|
|
|
|
|
|
|
|
## 9. 整改记录
|
|
|
|
|
|
|
|
|
|
|
|
### 2026-09-22:密码哈希统一为 bcrypt(对应建议 2、3)
|
|
|
|
|
|
|
|
|
|
|
|
| 位置 | 改动 | 说明 |
|
|
|
|
|
|
| --- | --- | --- |
|
|
|
|
|
|
| `internal/logic/forget/reset.go:32-43` | `utils.Md5(in.Password)` → `bcrypt.GenerateFromPassword(密码+新salt, bcrypt.DefaultCost)`,并在同一条 `Updates` 中同步写入新的 `salt` | 修复 P4:重置密码与登录校验(`bcrypt.CompareHashAndPassword(密码+sa.Salt)`)恢复一致;同时消除了"未加盐 MD5"与"重置后无法登录"两个问题 |
|
|
|
|
|
|
| `internal/logic/register/do.go:30` | `bcrypt.MinCost` → `bcrypt.DefaultCost` | 修复 P5(cost 4 → 10) |
|
|
|
|
|
|
| `internal/logic/account/set_password.go:35` | `bcrypt.MinCost` → `bcrypt.DefaultCost` | 修复 P5 |
|
|
|
|
|
|
| `internal/models/query.go:21` | `bcrypt.MinCost` → `bcrypt.DefaultCost` | 修复 P5(`InitData` 仍未被调用) |
|
|
|
|
|
|
|
|
|
|
|
|
验证:`gofmt` 无差异;`GOWORK=off go build ./...` 与 `go vet ./internal/...` 均通过。
|
|
|
|
|
|
|
|
|
|
|
|
> 历史 MD5 密码记录无法用 bcrypt 校验,需通过"重置密码"流程迁移(本次改动后重置将写入 bcrypt)。
|
|
|
|
|
|
|
|
|
|
|
|
### 2026-09-22:验证码校验、找回密码票据与 Token 前缀(对应 P1、P2、P3、P6)
|
|
|
|
|
|
|
|
|
|
|
|
| 编号 | 级别 | 问题 | 处理结果 |
|
|
|
|
|
|
| --- | --- | --- | --- |
|
|
|
|
|
|
| S1(原 P1/P2) | 高 | 验证码登录/快捷登录/注册/找回四条路径均不校验验证码 | 已修复:新增模块内验证码校验函数(`internal/vars/cache.go` 定义 Redis 键前缀常量,与 sender 侧 `/SMS/Code/` + 手机号规则一致),按 Redis 读取比对,**校验成功后删除键(一次性)**;四条路径全部接入 |
|
|
|
|
|
|
| S2(原 P3) | 高 | `Forget.Reset` 仅凭 `identity` 即可改任意账号密码 | 已修复:`Forget.Verify` 真实校验 `phone + code` 后写入一次性重置票据(Redis,带 TTL);`Forget.Reset` 先检查票据存在才允许改密,改密成功后立即删除票据(防重放)。未改 proto,用现有两步流程实现 |
|
|
|
|
|
|
| H3(原 P6) | 高 | Token 缓存键前缀 `/TOKEN/` 与 `/token/` 不一致 | 已修复:统一引用 `internal/vars.TokenPrefix` 常量,签发与校验/登出的键完全一致 |
|
|
|
|
|
|
|
|
|
|
|
|
> 本轮未修改 `proto/*.proto` 与 `pb/*.go`。
|
|
|
|
|
|
|
|
|
|
|
|
**遗留(未处理)**:P7 KYC 空 stub、P8 验证码接口未接入、P9 日志泄露 PII、P10 事务与错误处理缺失、全模块无测试,以及报告中「中/低」级别的各项。
|
|
|
|
|
|
|