Password Reset Token
Password Reset Token 是短期、单次、purpose-bound 的高熵恢复凭证,用来证明持有者控制已登记恢复通道;应使用 CSPRNG 生成、服务端只保存 digest/metadata、验证后原子消费,并防止 Host Header、Referrer、日志和重放泄漏。
[!info] related notes
- 所属 MOC: 安全 MOC
- Account Recovery: Password Reset & Account Recovery
- Password Policy: Password Policy
- Email Delivery: Email Verification & Email Change Security
- Session Consequence: Credential Change & Session Revocation
Password Reset Token
一句话定义
Password Reset Token 是:
临时恢复凭证,不是长期 Session,也不是 Access Token。
典型:
random token → send via side channel → user presents token → server verifies → token consumed → restricted password reset allowed
生成
使用 CSPRNG。
不要使用:
- sequential ID;
- timestamp;
- UUIDv1;
- predictable database id;
- Math.random。
Token 应拥有足够 entropy,使在线 brute force 在有效期内不可行。
服务端不要保存原始 Token
更稳的模式:
raw token → send to user
H(raw token) → store digest
数据库/Redis 保存:
- token_digest;
- user_id;
- purpose;
- expires_at;
- used_at / status;
- issued_at;
- optional request context/version。
这样数据库泄漏不会直接得到可用 reset bearer token。
Single Use
Token consumption 应原子化:
validate digest AND expires_at > now AND used_at IS NULL → mark used → create restricted recovery state / perform reset
并发两个请求不能都成功。
Short Lifetime
Reset token 应短期有效。
具体 TTL 要根据:
- email delivery latency;
- UX;
- account value;
- risk;
决定。
不能把“24h”或“15min”当成所有系统的永恒常数。
Purpose Binding
Token 要绑定:
purpose = password_reset
不要让同一个 generic token 同时执行:
- verify email;
- reset password;
- change email;
- disable MFA。
不同 security action 应有不同 purpose / token type。
User Binding
Token 必须关联明确 account。
不要设计:
token + client-provided arbitrary user_id → reset that user
服务端应该从 token record 自己解析目标 account。
URL Construction
生成 reset URL 时不要盲目信任请求中的 Host header。
错误:
https://{request.Host}/reset?token=…
攻击者可能制造 Host Header Injection,让邮件里的 link 指向 attacker domain。
更稳:
- configured trusted frontend origin;
- allowlisted origin;
- canonical application URL。
HTTPS
Reset URL 必须使用 HTTPS。
Token 是 bearer-like secret。
被网络观察者窃取后,通常可直接执行 recovery action。
Referrer Leakage
如果 reset token 位于 query string:
/reset?token=SECRET
页面加载第三方资源或跳转时,token 可能通过 Referer 泄漏。
防御包括:
- restrictive Referrer-Policy;
- reset page 少加载第三方资源;
- token 验证后尽快换成 restricted server-side recovery session;
- 不把 token 长期保留在 URL/history。
Logs / Analytics
不要记录:
- full reset URL;
- raw token;
- token query parameter。
检查:
- reverse proxy access log;
- APM;
- analytics;
- error tracker;
- frontend telemetry。
JWT Reset Token
可以使用 JWT,但通常没有必要。
随机 opaque token + server-side record 更自然地支持:
- single-use;
- revoke;
- purpose;
- consume atomically。
自包含 JWT 如果不保存 server state,会让“单次使用”更难实现。
Verification Rate Limit
即使 token 很长,也应限制:
- token verification;
- PIN/code attempts;
- per-account recovery attempts。
短 PIN 尤其必须严格 rate limit。
Token Rotation / Reissue
再次 request reset 时可以:
- 使旧 token 失效;
- 或允许少量并行 token。
必须明确 policy。
高安全系统通常更偏:
new reset request → invalidate older unconsumed reset tokens
减少有效 credential 数量。
学习检查
- Reset Token 与 Access Token / Session 有什么区别?
- 为什么 server 更适合保存 reset token digest,而不是 raw token?
- single-use 为什么需要原子消费?
- purpose binding 能防什么错误?
- Host Header Injection 怎样影响 reset email?
- query-string token 为什么有 Referrer/log leakage 风险?
- JWT 为什么未必适合 password reset?
- 为什么高熵 token 仍应有 verification rate limit?