post_login_api_compliance_audit.md 8.7 KB

登录后接口合规性审计(五条规则)

审计日期: 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 对应关系。