跳到主内容
Autional 徽标
Security9 分钟#账号找回#恢复码#安全#NIST

账号找回怎么设计:恢复码、备用通道与防冒充

先讲一个处境:小满换了手机号,旧号停机;某天她忘了密码,想走「忘记密码」——验证码发到旧手机号上,收不到;翻出注册邮箱,发现邮箱密码也早忘了。

她还进得去自己的账号吗?

大多数系统的答案是:很难,或者再也进不去。这就是账号恢复设计要解决的第一件事——你有没有提前给自己留好「第二条路」。

但恢复流程还有另一半,而且更危险:每一步恢复机制都是绕开日常钥匙的后门。后门既可以救你,也可以被人利用。 「忘记密码」入口一旦设计不当,就从救命通道变成了攻击者的「账号探测器」。

「恢复」为什么必须比登录更重

一句话原则:恢复只认你事先留的通道。靠记性、靠能被查到的信息,都不算。

这条原则有它的反面教材。Colonial Pipeline(2021):据公司声明与国会听证的公开信息,攻击入口是一个已泄露的旧密码,加上未启用的多因素认证,导致美国东岸燃油运输一度中断。攻击者没有「攻破」哪道新防线——他们用的是一扇早就该作废的门。

对恢复设计的启示有两层:

  • 旧凭据不及时作废,就一直是扇敞着的门——恢复完成后撤销旧会话、旧凭据,是闭环的一部分;
  • 恢复流程自身不能成为最弱的一环——它验证的信息,必须是你提前留好的、别人查不到的。

(注:该事件的具体技术细节在公开报告中披露有限,本文不做超出公开信息的技术推断。)

四条「第二条路」

企业系统里可用的恢复通道,主流是四种,加一道最后的人工申诉:

维度 ① 邮箱码 ② 短信码 ③ 恢复码 ④ 恢复联系人
是什么 发到注册邮箱的一条码 发到绑定手机的一条码 注册时给的一串一次性离线码 你提前指定的人
怎么用 收信 → 填码 → 重设 收短信 → 填码 → 重设 取出一张 → 填 → 重设 真到走不动时出面搭手
最稳的时候 邮箱还进得去 手机号还在用 手机、邮箱都丢时,它还在 你两条路都断了
什么时候失效 邮箱被盗 / 登不上 换号 / 停机(本文开头的小满就栽这) 和账号存在一起、一起丢 选错了人
一句话 靠邮箱 靠手机 靠一张纸 靠一个人

四条路的设计细节,值得按标准对齐:

  • 恢复码是一次性的。NIST SP 800-63B-4(2025-07 发布,取代 800-63B-3)§3.1.2 对「查阅式秘密」(look-up secret)的要求:只能成功使用一次(§3.1.2.2)、至少 6 位十进制数字(§3.1.2.1)、服务端须做哈希存储。§4.2 给出的是账号找回(account recovery)的完整框架,其中 §4.2.1 列出「恢复码」这一类:发给用户、用于在无法正常认证时找回账号的秘密。
  • 短信码属于带外认证(§3.1.3 Out-of-Band)。标准同时说了一句常被误读的话:邮箱不得用于带外认证——但 §3.1.3.1 明确,作为恢复/确认码发到邮箱不受此禁令影响(§4.2.1.2 是恢复侧的对应条款)。所以「邮箱码」的定位是恢复码,不是 OOB 认证器。
  • 安全问题(KBA)不该再用。§3.1.1.2 写得很直白:验证方不得提示用户使用基于知识的认证(「你第一只宠物叫什么?」)或安全问题。答案别人查得到——用安全问题做恢复,等于把钥匙挂门上。
  • 恢复接口必须限流(§3.2.2 对限流的一般要求):恢复请求、验证码尝试、重设提交,每一段都要有速率限制和锁定策略。
flowchart TD
    A["发起恢复:request"] --> B["统一响应<br/>不暴露账号是否存在"]
    B --> C{"验证凭证<br/>邮箱码 / 短信码 / 恢复码"}
    C -->|"失败 · 超限"| D["限流锁定<br/>冷却后再试"]
    C -->|"通过"| E["完成恢复:complete<br/>重设密码"]
    E --> F["撤销该账号全部旧会话<br/>并通知本人"]

图 1:恢复主线——request → verify → complete;统一响应挡住枚举,限流挡住硬猜,完成即撤会话、通知本人。

防冒充:四个要件

恢复流程的安全,不体现在「能不能走通」,而体现在「冒充者能不能走通」。四件事缺一不可:

① 提前留不止一条路。 只留一条路,那条路一丢就只剩最重的人工申诉。至少两条,且别放在同一个地方——邮箱和手机就是两个地方;恢复码拍照存相册,就变成了「和账号一起丢」。

② 恢复码:一次性 + 离线存。 一次性意味着泄露了也难复用(用过即废);离线意味着手机和账号一起丢时它还在。十条码、用一张少一张,是这套机制的正常形态。

③ 恢复完成后,踢掉旧的登录,并通知本人。 这条最容易被漏掉,后果也最直接:如果冒充者在你走恢复流程之前已经登进来了,你改了密码也踢不掉他——旧会话还在,门没关。所以恢复完成后必须撤销该账号的所有会话,并给本人发一条「密码刚被改过」的通知。

④ 统一提示 + 限流。 「忘记密码」接口若对不存在的账号回「账号不存在」、对存在的账号回「验证码已发送」,它就成了一个账号探测器——冒充者靠「试一下」就能筛出哪些邮箱注册过。正确做法:在不在,都回同一句话。 再加上限流,让「一直猜」变得不可行。

三个最常见的设计坑

  1. 只留一条路。 邮箱手机一起丢就完了——恢复通道要设计成冗余的;
  2. 恢复码截图存相册。 和账号存在同一台设备上,等于没留;
  3. 恢复完不踢旧的。 改完密码,冒充者的旧会话还挂在系统里。

还有一条设计哲学层面的提醒:没有可靠第二条路时,安全的做法是走更重的人工核验,而不是放低门槛。 人工申诉慢、体验差,但慢本身就是防御——恢复流程的默认状态应该是「更重」,而不是「更方便」。

Autional 的做法

Autional 提供三条恢复路径,以及贯穿全程的限流与审计:

  1. 忘记密码(forgot-password):向已绑定的邮箱或手机发送验证码,验证通过后重设密码;对未注册账号返回统一提示,不暴露账号是否存在。
  2. 恢复流程(recovery/request → verify → complete):独立于普通改密的恢复链路;完成后自动撤销该账号的全部会话,并发送密码变更通知——确保「旧钥匙全作废」。
  3. 恢复联系人(Recovery Contacts):支持增删(GetRecoveryContacts / Add / Remove),供前两条路都不可用时启用。

配套机制:

  • 全程限流 + 审计:恢复请求、验证码校验都有速率限制;关键动作落审计事件(recovery.requested / code_verified / recovery/complete),事后可追溯;
  • 恢复码加密存储、一次性、有数量上限(默认上限 10 个)。

诚实边界:以上默认值(如登录锁定策略 LockoutAttempts=5 / LockoutDurationMinutes=30、恢复码上限)是产品选择,不是行业硬性规定;各系统的恢复流程实现差异很大,公开资料通常不完整,不逐家断言。另外,恢复请求返回的掩码目标(MaskedTarget)是否构成新的枚举面,以及恢复码的存储形态(产品侧为加密存储,标准字面要求为哈希存储)的对应关系,都属于我们持续核实的项目。

常见误区

  • 「找回就是点一下『忘记密码』」——只有你提前留了第二条路才成立;没留或留的也没了,就得走最重的一道;
  • 「安全问题更保险」——答案别人查得到,标准已明确不用;
  • 「恢复完就没事了」——不撤销旧的登录,冒充者可能还登着,你改密码也白改;
  • 「恢复码拍照存手机就行」——跟账号存在一起,等于没留;
  • 「恢复流程慢是体验差」——恢复是你最弱的那一刻,难一点,才挡得住冒充的人。

结语

登录靠密码,恢复靠第二条路。判据两句话,现在就可以检查自己的账号:

我有没有第二条路?我有没有把旧的登录收回来?

下次再遇到「进不去了」,别急着怪自己运气差——恢复比登录难,它本来就是你最弱的那一刻;难一点,才挡得住冒充的人。

相关阅读

准备好给应用加上身份层了吗?

Autional 提供认证、组织、访问控制、审计与开发者 API —— 面向 AI 生成应用的身份层。