密码重置功能几乎是每个 Web 应用的标配,但正是这个"贴心"的功能,成了攻击者最喜欢利用的入口。从猜答案到劫持邮箱,从批量重置到绕过多因素认证,密码重置漏洞的攻击面远比想象中大。
密码重置的常见实现方式
现代应用通常提供以下几种密码重置途径:
- 邮箱验证:发送重置链接或验证码到注册邮箱
- 短信验证:发送验证码到绑定手机
- 安全问题:回答预设的密保问题
- 备用联系方式:通过备用邮箱或手机号
每种方式都有自己的安全隐患,组合使用时问题更复杂。
邮箱重置链接的攻击
1. 令牌可预测
最基础的问题是重置令牌(token)的生成不够随机。如果令牌是简单的递增数字、时间戳哈希,或者伪随机数生成器质量差,攻击者可以枚举有效的令牌。
GET /reset-password?token=123456 ← 可枚举
GET /reset-password?token=abc123 ← 字典可猜安全的令牌应该使用加密安全的随机数生成器(CSPRNG),长度至少 128 位(16 字节),以 URL-safe base64 或 hex 编码呈现。
2. 令牌未绑定用户
有些实现中,重置令牌只验证有效性,不验证它是否属于当前请求重置的用户。攻击者可以用自己的邮箱申请重置,拿到令牌后,用该令牌重置其他用户的密码。
# 攻击流程
1. 攻击者用自己的账号申请重置,获得 token_A
2. 攻击者用 token_A 去重置 victim 的密码
3. 系统只验证 token_A 是否有效,不检查所属用户修复:令牌必须与用户 ID 绑定,验证时同时检查。
3. 令牌过期时间过长
重置链接通常设定 1 小时或 24 小时有效期。但如果攻击者通过某种方式(如邮件服务器日志、网络嗅探)获取了历史令牌,过长的有效期给了他们充足的利用窗口。
建议:重置令牌有效期控制在 15-30 分钟,且使用后立即失效(一次性)。
4. 邮件头泄露信息
重置邮件的 Received 头可能泄露用户的真实 IP、邮件客户端信息,甚至内部网络拓扑。攻击者收集这些信息可以辅助后续社工攻击。
5. 邮件劫持与会话固定
如果攻击者已经入侵了用户的邮箱(通过钓鱼、弱口令、泄露数据库),密码重置就成了顺理成章的下一步。更隐蔽的是,攻击者可以:
- 在用户申请重置前,先用自己的设备发起重置请求
- 劫持重置邮件中的链接(某些邮件客户端会预加载链接)
- 当用户真正点击时,攻击者已经通过预加载完成了重置
短信验证码的攻击
1. 验证码暴力破解
4 位或 6 位数字验证码的搜索空间很小。如果没有速率限制,攻击者可以在几分钟内枚举完所有可能:
# 6 位验证码,1000000 种可能
for code in range(1000000):
response = requests.post("/reset-password", data={"code": f"{code:06d}"})
if "success" in response.text:
print(f"Found code: {code}")
break修复:实施严格的速率限制——每个手机号每小时最多 3-5 次尝试,超过则锁定或要求人工验证。
2. SIM 卡交换攻击(SIM Swap)
这是运营商层面的攻击。攻击者通过社工或贿赂运营商客服,将受害者的手机号转移到自己控制的 SIM 卡上。一旦成功,所有短信验证码都会发送到攻击者手机。
防御:对敏感操作(如密码重置、大额转账)要求额外的验证因素,不要仅依赖短信。
3. 短信拦截与 SS7 攻击
- 短信拦截工具:某些恶意软件可以读取设备上的短信
- SS7 协议漏洞:电信网络的 SS7 协议存在已知漏洞,攻击者可以拦截或重定向短信
4. 语音验证码绕过
有些应用提供"收不到短信?改用语音电话"的选项。攻击者可以利用这个:
- 触发语音验证码到受害者手机
- 同时给受害者打电话(伪装成客服),诱导他们报出验证码
- 或者利用受害者语音信箱的默认密码,直接收听留言
安全问题的弱点
1. 答案可预测或公开
"你母亲的名字是什么?"——在很多国家,这是公开记录。"你的第一辆车是什么?"——社交媒体上的照片可能泄露。
2. 答案可枚举
安全问题通常有有限的可接受答案。攻击者可以收集常见答案构建字典:
常见宠物名:Max, Bella, Charlie, Lucy...
常见城市:北京, 上海, 广州, 深圳...3. 多个账户答案相同
用户倾向于在所有网站使用相同的安全问题答案。一旦一个网站的数据泄露,攻击者可以用这些答案尝试其他网站。
4. 答案存储明文
有些系统以明文或弱哈希存储安全问题答案。数据库泄露后,攻击者直接获得答案。
密码重置流程的逻辑漏洞
1. 步骤跳过
多步骤的密码重置流程(验证身份 → 输入新密码 → 确认)如果没有严格的步骤校验,攻击者可能直接跳到最后一步:
POST /reset-password/step3 ← 跳过 step1 和 step2修复:每个步骤都要验证前置步骤是否已完成,使用服务器端会话状态跟踪进度。
2. 响应差异泄露信息
"该邮箱未注册" vs "重置邮件已发送"——前者告诉攻击者这个邮箱不在系统中,可以用来枚举有效用户。
修复:无论邮箱是否存在,都返回相同的模糊消息:"如果该邮箱已注册,您将收到重置邮件。"
3. 并发条件竞争
如果应用允许多个重置请求同时存在,可能出现条件竞争:
- 用户 A 申请重置,获得 token_A
- 用户 B(攻击者)同时申请重置同一账号,获得 token_B
- 系统可能只保存最后一个令牌,或者两个都有效
- 攻击者使用 token_B 重置密码,用户 A 使用 token_A 时失败或覆盖
4. 旧密码未失效
重置密码后,旧密码应该立即失效。但有些系统实现中,旧密码仍然可用,直到用户主动退出所有会话。
多因素认证的绕过
1. 重置流程绕过 MFA
有些应用在正常登录时要求 MFA,但在密码重置流程中却不要求。攻击者通过重置密码获得的新会话,直接绕过了 MFA 保护。
2. 备用码滥用
MFA 的备用恢复码如果和重置流程结合不当,可能成为绕过路径。例如,输入任意恢复码就可以跳过验证步骤直接重置。
3. 设备信任滥用
"信任此设备 30 天"功能如果在重置流程中被错误地信任,攻击者重置密码后自动获得受信任设备状态。
真实攻击案例
案例 1:Twitter 2019 密码重置漏洞
Twitter 的密码重置 API 在特定条件下会暴露用户的绑定手机号。攻击者通过提交特定格式的请求,可以获取与账号关联的手机号,为 SIM 交换攻击做准备。
案例 2:Facebook 邮件劫持
Facebook 的密码重置邮件在某些邮件客户端中,由于链接预加载机制,攻击者可以通过发送大量重置请求,利用邮件服务商的预加载功能"点击"重置链接,从而劫持账号。
案例 3:某银行语音验证码社工
攻击者打电话给银行客服,声称收不到短信验证码,要求改用语音。同时给受害者打电话,冒充银行工作人员,声称系统升级需要验证,诱导受害者报出收到的语音验证码。双重社工成功重置密码并转账。
防御策略
1. 令牌设计
- 使用加密安全随机数生成器(CSPRNG)
- 长度至少 128 位
- 与用户 ID 严格绑定
- 一次性使用,验证后立即失效
- 有效期 15-30 分钟
- 在 URL 中传输时使用 HTTPS
2. 传输安全
- 重置链接必须使用 HTTPS
- 邮件内容不要包含敏感信息(如用户 ID、部分密码)
- 考虑在邮件中提示:"如果您没有请求重置,请忽略此邮件"
3. 速率限制
- 每个邮箱/手机号每小时最多 3 次重置请求
- 同一 IP 地址每小时最多 10 次请求
- 超过限制后要求 CAPTCHA 或人工审核
- 对连续失败尝试实施指数退避
4. 多因素验证
- 敏感账号在密码重置时要求额外的验证因素
- 不要仅依赖单一因素(邮箱或短信)
- 考虑使用已登录设备确认、生物识别等强验证
5. 通知与监控
- 密码重置成功后,立即通知用户(邮件+短信)
- 记录所有重置请求日志,包括 IP、时间、设备信息
- 对异常模式(如短时间内大量请求、跨地域请求)触发告警
- 提供用户自助查看最近登录和重置活动的能力
6. 替代方案
- 魔法链接(Magic Link):发送一次性登录链接,不直接重置密码
- 已登录设备确认:在新设备上重置时,要求已登录设备确认
- 账户恢复流程:设计专门的人工审核恢复流程,用于极端情况
7. 用户教育
- 提醒用户保护邮箱和手机号的安全
- 建议使用强密码和 MFA 保护邮箱
- 警惕声称来自服务提供商的钓鱼电话和短信
总结
密码重置是安全与便利的平衡点。过于严格会让用户流失,过于宽松则给攻击者可乘之机。关键原则:
- 假设攻击者已经知道用户的邮箱或手机号——设计防御时不要把这两者当作绝对可信的
- 多层验证优于单层验证——组合使用多种验证因素
- 监控与响应——及时发现异常模式并快速响应
- 最小权限原则——重置流程只授予必要的权限,重置后要求重新验证敏感操作
密码重置不是"辅助功能",它是认证体系的核心组成部分,值得投入同等的安全设计精力。