文件
hyb-oa/.trellis/tasks/archive/2026-07/07-17-auth-challenge-trusted-proxy-hardening/design.md
T

5.5 KiB

认证验证码与可信代理安全设计

Security Invariants

  1. 同一账号同一时刻最多一枚可验证的密码重置码。
  2. 失败次数与账号绑定,不与 IP、单个验证码或应用进程绑定。
  3. 验证码只能原子消费一次;密码变化与会话撤销不可拆分。
  4. 应用只信任实际反向代理生成的客户端地址。
  5. 未知账号、停用账号、锁定账号和无效验证码不能形成账号枚举信号。
  6. 已用旧密码完成事务外校验的并发登录,不得在密码重置提交后创建可刷新的新会话。

Data Design

  • PasswordResetCode 增加 invalidated_at 与 attempts_count,验证码查询必须要求未使用、未失效、未过期。
  • Employee 增加密码恢复专用失败计数、失败窗口、锁定截止、申请计数、申请窗口和最近申请时间;不复用密码登录失败字段。
  • Employee 同时增加邮箱验证码登录的账号级失败计数、失败窗口、锁定截止和最近申请时间;重新申请不得绕过累计失败边界。
  • 第一版不增加 partial unique index。申请事务通过员工行 FOR UPDATE 串行化并先失效旧码;验证事务通过同一账号锁和条件更新确保单次消费。
  • 迁移将所有现有未使用密码重置码标记为失效,避免弱历史码继续生效。
  • 历史 migration 对 password_reset_codes.employee_id 使用 ON DELETE CASCADE,而当前 schema 声明 Restrict;这是本任务之前已经存在的漂移。本次手写 additive migration 不改外键,最终删除语义另行裁决,禁止由 migrate dev 静默夹带修复。

Request Flow

  1. 规范化邮箱并查找活跃员工。
  2. 对未知/停用账号直接返回通用成功;不得写含邮箱的诊断日志。
  3. 事务锁员工行,读取恢复状态。
  4. 若处于账号锁、冷却或申请窗口上限,只更新必要的安全审计并返回通用成功。
  5. 失效旧码,生成 CSPRNG 验证码和用途域分离 HMAC,创建唯一当前码,原子更新申请状态。
  6. 事务提交后发送通知;外部邮件失败不回滚安全状态,也不泄露账号存在性。

Verify Flow

  1. 使用相同的通用错误处理未知、停用、锁定、过期和错误码。
  2. 事务锁员工行并读取当前有效验证码。
  3. 使用 timing-safe 比较 HMAC;失败时原子累计账号和验证码尝试,达到阈值设置锁定并失效验证码。
  4. 成功时以 used_at IS NULL AND invalidated_at IS NULL 条件消费,要求更新数为 1。
  5. 在同一事务内更新密码、增加 token 版本、撤销 refresh session、失效该账号所有恢复码和邮箱登录码、写成功审计。

Email Login Flow

  1. 申请与验证均先锁员工行;申请受账号冷却和邮箱验证码专用锁控制,并在创建新码前把旧未用码标记为已失效。
  2. 验证失败在事务内同时累计账号和当前码次数;达到阈值时锁定账号级邮箱验证码入口,重新申请不清零。
  3. 验证成功条件消费当前码、清理邮箱验证码失败状态、创建 refresh session 并写审计,事务提交后才设置 cookie。
  4. 普通密码登录在创建 refresh session 前,以初读 password_hash + token_version 做条件更新;密码重置先提交时旧登录必须失败,登录先提交时其会话由随后重置撤销。
  5. 用户主动改密和管理员重置密码同样撤销 refresh session、失效恢复码与邮箱验证码并清理账号级挑战状态,避免旧验证码覆盖新的密码安全事件。

Trusted Proxy Design

  • getConfig() 提供显式 proxy trust 配置;开发/测试默认 false,部署配置为受控的一跳或 CIDR。
  • Fastify 构造使用解析后的值,禁止生产回落到 true。
  • Caddy 是当前事实部署代理;Nginx 模板作为兼容配置同样覆盖 XFF。
  • Compose 的 server 映射改为宿主回环地址;数据库和对象存储端口的进一步收口单独记录,不扩大本 task 的认证验收。

Error And Audit Contract

  • 申请接口始终返回相同成功 envelope。
  • 验证失败统一返回 VALID_001 和稳定的 invalid_or_expired,不区分账号状态。
  • 审计可记录 employee ID、计数、锁定结果和可信 IP,不记录邮箱、验证码、HMAC、密码或原始请求体。
  • 生产配置不得记录验证码;启动期发现开发显示开关即拒绝启动。

Migration And Rollback

  • migration 只新增字段/索引并失效短期验证码,不删除业务数据。
  • 发布顺序必须先在受控环境设置强 pepper 与单跳代理配置、由边缘暂停 /api/auth/forgot-password、/api/auth/reset-password、/api/auth/email-login/code、/api/auth/email-login/verify 四个 challenge 端点,再用新镜像执行配置预检和 additive migration,最后切换应用;不得先启动依赖新增列的新应用。
  • 代码发布失败时可回滚应用,新增字段对旧版本无害;但边缘必须继续关闭上述四个端点,旧代码不能重新对外提供弱流程。
  • 不执行 migrate reset,不在本任务连接或修改生产数据库。

Test Matrix

  • 申请:未知/停用/活跃、冷却、窗口上限、串行与并发、多实例语义。
  • 验证:错误次数、锁定、锁后重申、过期、旧码、正确码并发消费、统一响应。
  • 会话:旧 access/refresh、邮箱验证码、旧密码、新密码和审计。
  • 代理:无代理、可信一跳、伪造单/多值 XFF、直连端口边界。
  • 配置:生产缺 pepper、生产显示验证码、env contract 和回滚配置。