← 返回博客
安全密码重置事务邮件开发者
密码重置邮件的安全实现
token 生成、哈希存储、过期策略、防滥用和单次使用的完整安全实现。
更新于 2026年10月12日

密码重置邮件的安全实现
密码重置邮件是攻击者最常盯上的入口之一。实现不当,账号就被接管了。
Token 的生成和存储
重置 token 必须用密码学安全的随机数生成器,长度不低于 32 字节,用 URL-safe Base64 编码:
const crypto = require('crypto');
const token = crypto.randomBytes(32).toString('base64url');
存储时不要明文存 token,存它的哈希:
const hash = crypto.createHash('sha256').update(token).digest('hex');
await db.query(
'INSERT INTO password_resets (user_id, token_hash, expires_at) VALUES ($1, $2, NOW() + INTERVAL '15 minutes')',
[userId, hash]
);
发送邮件
await rovela.emails.send({
from: 'security@yourdomain.com',
to: user.email,
subject: '重置你的密码',
html: resetTemplate({ resetUrl: `https://yourdomain.com/reset-password?token=${token}` }),
text: `点击链接重置密码(15 分钟内有效):https://yourdomain.com/reset-password?token=${token}`,
});
过期和单次使用
- 15 分钟过期:超时作废,要求重新申请
- 使用后立即删除:token 用过一次就从数据库删掉
- 邮箱不存在时也要返回"已发送":不要让攻击者枚举账号
// 不管邮箱存不存在,都返回同样的响应
res.json({ message: '如果该邮箱已注册,你将收到重置邮件' });
防滥用
- 同一邮箱 1 小时内最多允许申请 3 次
- 记录请求日志,包含 IP
- 重置成功后通知用户(发一封"你的密码已被修改"邮件)
链接格式
重置链接要用 HTTPS,token 放在查询参数里,不要放在 hash 里(hash 不会发到服务器)。链接格式:https://yourdomain.com/reset-password?token=<token>