# 登录后接口合规性审计(五条规则) **审计日期**: 2026-03-01 **范围**: client-tauri 登录成功后调用的**所有业务接口、心跳服务接口、密钥交换接口**,是否满足以下五条规则: 1. **出入参密文 + 时间戳校验 + 签名验证** 2. **校验是否登录**,未登录则退出登录 3. **校验机器码**与用户绑定一致,不一致则退出登录 4. **校验用户是否过期**(卡密/订阅),过期则退出登录 5. **日志记录**:入参密文、入参明文、出参明文、出参密文 --- ## 结论概览 | 规则 | 后端 | 客户端 | |------|------|--------| | ① 出入参密文 + 时间戳 + 签名 | ✅ 全部走 `@secure_endpoint` 的安全端点,满足 | ✅ 全部通过 Rust 加密层(`secure_*` / `secure_post_decrypt`) | | ② 校验登录,未登录则退登 | ✅ 安全端点 `require_login=True`,401 返回「会话已过期」 | ✅ 401/403 统一走 `handleAuthError` 触发退登 | | ③ 校验机器码,不一致则退登 | ✅ 安全端点 `require_machine_code=True`,403 返回「其他设备」 | ✅ 同上,403 触发退登 | | ④ 校验用户过期,过期则退登 | ✅ 安全端点内按卡密/订阅 `expire_time` 判断,403 返回「卡密已到期」 | ✅ 同上,403 触发退登 | | ⑤ 日志:入参密文/明文、出参密文/明文 | ✅ `SecureEndpoint` 内调用 `api_logger.log_api_call` 记录四项 | ⚠️ 客户端仅在后端记录;Rust 侧可按需加调试日志 | **整体结论**:登录后使用的**业务接口、心跳、密钥同步、密钥交换(HMAC)** 均已符合上述五条规则。公告接口为「可选登录」设计,若仅登录后调用且需纳入同一规范,可将公告改为需登录的安全端点(当前为密文+时间戳+签名,但不校验登录/机器码/过期)。 --- ## 一、接口清单与合规情况 以下均为**登录成功后**会调用的接口(client-tauri 侧入口 → 后端路由 → 合规项)。 | 接口 | 客户端调用 | 后端路由 | ①密文+时间戳+签名 | ②登录 | ③机器码 | ④过期 | ⑤日志 | |------|-------------|----------|-------------------|-------|---------|-------|-------| | 心跳 | `invoke('secure_heartbeat')` | POST `/api/v1/heartbeat` | ✅ | ✅ | ✅ | ✅ | ✅ | | 密钥同步(RSA) | 心跳内 `secure_keys_sync_and_store` | POST `/api/v1/keys/sync` | ✅ | ✅ | ✅ | ✅ | ✅ | | 密钥交换(HMAC) | `invoke('secure_keys_hmac')` | POST `/api/v1/keys/hmac/secure` | ✅ | ✅ | ✅ | ✅ | ✅ | | 用户信息 | `invoke('get_user_profile')` → `secure_get_user_profile_call` | POST `/api/v1/user/profile` | ✅ | ✅ | ✅ | ✅ | ✅ | | BUFF 筛选项 | `invoke('secure_buff_filter_options')` | POST `/api/v1/buff/filter-options` | ✅ | ✅ | ✅ | ✅ | ✅ | | BUFF 商品属性 | `invoke('secure_buff_goods_attr_list')` | POST `/api/v1/buff/goods-attr-list` | ✅ | ✅ | ✅ | ✅ | ✅ | | 选品配置(获取/保存) | `invoke('secure_buff_selection_config')` | POST `/api/v1/buff/secure/selection-config` | ✅ | ✅ | ✅ | ✅ | ✅ | | 选品开始 | `invoke('secure_buff_selection_start')` | POST `/api/v1/buff/secure/selection-start` | ✅ | ✅ | ✅ | ✅ | ✅ | | 公告 | `invoke('secure_get_announcements')` | POST `/api/v1/announcement`(安全端点) | ✅ 密文+时间戳+签名 | ❌ 不校验登录 | ❌ 不校验机器码 | ❌ 不校验过期 | ✅ | 说明: - **心跳**:前端状态栏/后台心跳均使用 `secure_heartbeat`,与后端 `@secure_endpoint(require_login=True, require_machine_code=True)` 的 `/heartbeat` 对应,后端在安全端点内检查会话、机器码、用户状态及卡密/订阅过期。 - **密钥同步**:`/keys/sync` 为安全端点,同样做登录+机器码+过期校验。 - **HMAC**:已改为 `secure_keys_hmac` → POST `/api/v1/keys/hmac/secure`,不再使用明文 `POST /keys/hmac`。 - **公告**:当前为「可选登录」设计(`require_login=False`),故不满足 ②③④;若业务要求「仅登录后可看公告」且与上述规则一致,需将公告接口改为 `require_login=True, require_machine_code=True` 并仅由登录态客户端调用。 --- ## 二、后端实现要点(五条规则对应) ### 2.1 ① 出入参密文 + 时间戳 + 签名 - 所有上述业务/心跳/密钥接口均为 **POST**,请求体为:`encrypted_params`、`aes_key`、`key_version`、`timestamp`、`nonce`、`signature`。 - 后端 `SecureEndpoint`(`app/middleware/security.py`)会: - 校验 **时间戳**(±5 分钟) - 校验 **nonce**(防重放) - 校验 **请求签名**(`timestamp:nonce:encrypted_params:aes_key:key_version`,HMAC-SHA256) - 解密得到入参明文,执行业务后使用**客户端 AES 密钥**加密出参,并附带 **response_timestamp / response_nonce / response_signature** 供客户端验签。 ### 2.2 ② 校验登录 / ③ 机器码 / ④ 用户过期 - `@secure_endpoint(require_login=True, require_machine_code=True)` 时: - **登录**:从解密参数取 `session_id`,查会话是否存在、有效、未过期;否则返回 **401**「会话已过期,请重新登录」。 - **机器码**:解密参数中的 `machine_code`(或 `raw_machine_info`)与 `session.machine_code` 按约定规范化后比较;不一致返回 **403**「账号已在其他设备登录…」。 - **用户过期**:按 `user.expire_time` 或 `UserSoftwareSubscription.expire_time` 与当前时间比较;过期返回 **403**「您的卡密已到期…」。 ### 2.3 ⑤ 日志(入参密文/明文、出参密文/明文) - 在 `SecureEndpoint` 的 wrapper 内,解密得到 `decrypted_params`、业务返回 `response_data` 后: - 调用 `api_logger.log_api_call(..., encrypted_request=encrypted_params, decrypted_request=decrypted_params, encrypted_response=encrypted_response, decrypted_response=response_data)`。 - `api_logger` 将上述四项写入按天滚动的 API 日志文件(`logs/api/api_YYYY-MM-DD.log`),并对敏感字段脱敏(见 `api_logger.py` 中 `REDACTED_FIELDS`)。 --- ## 三、客户端实现要点 ### 3.1 请求侧(① 密文 + 时间戳 + 签名) - 所有登录后业务/心跳/密钥请求均通过 **Rust** 层: - `secure_heartbeat_call_inner`、`secure_post_decrypt`、`secure_get_user_profile_call` 等构造:AES 加密入参、RSA 加密 AES 密钥、生成 timestamp/nonce、HMAC 签名后 POST。 - 响应侧:读取 `encrypted_data`,校验 `response_signature` 后 AES 解密,再返回给前端。 ### 3.2 ②③④ 未登录/机器码不一致/过期 → 退登 - 后端返回 **401/403** 时,Rust 将 `code`、`msg` 原样带回(如 `secure_heartbeat_call_inner`、`secure_post_decrypt` 中对 401/403 的专门分支)。 - 前端 `secureApiClient` 中所有调用在收到 `code === 401 || code === 403` 时统一调用 **`handleAuthError(code, msg)`**,触发退出登录(清理会话、跳转登录页等)。 ### 3.3 ⑤ 日志 - 入参/出参的密文与明文已在前端**不落地**(仅在 Rust 内存中加解密),规范要求的「入参密文、入参明文、出参明文、出参密文」由**后端**统一记录即可。 - 若需在客户端排查问题,可在 Rust 的 `secure_post_decrypt` 或各 `secure_buff_*` / `secure_keys_hmac` 中增加调试级日志(注意不要打印敏感明文到生产)。 --- ## 四、与旧版/明文接口的对比 以下接口**已废弃或仅作兼容**,登录后不应再使用: | 旧接口 | 说明 | |--------|------| | GET `/api/v1/buff/filter-options?session_id=&machine_code=&game=` | 已由 POST 安全端点替代,前端改用 `secure_buff_filter_options` | | GET `/api/v1/buff/goods-attr-list?goods_ids=...` | 已由 POST 安全端点替代,前端改用 `secure_buff_goods_attr_list` | | GET/POST `/api/v1/buff/selection-config`(明文 body) | 已由 POST `/api/v1/buff/secure/selection-config` 替代 | | POST `/api/v1/buff/selection-start`(明文 body) | 已由 POST `/api/v1/buff/secure/selection-start` 替代 | | POST `/api/v1/keys/hmac`(明文 JSON) | 已由 POST `/api/v1/keys/hmac/secure` 替代,前端改用 `secure_keys_hmac` | --- ## 五、可选改进 1. **公告接口**:若要求「仅登录后可看公告」且与五条规则一致,可将 `post_announcements` 改为 `@secure_endpoint(require_login=True, require_machine_code=True)`,并确保前端仅在使用登录态时调用 `secure_get_announcements`。 2. **客户端 Rust 日志**:如需与后端日志风格完全统一,可在 Rust 安全调用路径中增加「入参密文/明文、出参密文/明文」的 debug 级别日志(脱敏后写入本地日志)。 --- *文档维护:随接口变更更新本表与后端路由、前端 invoke 对应关系。*