MFA(Multi-Factor Authentication,多因素认证)的概念已经普及了近二十年。从早期的 RSA SecurID 硬件令牌,到今天的 TOTP 软件令牌、FIDO2/WebAuthn 安全密钥、Passkey 跨设备同步——MFA 领域的技术迭代速度在安全工程中是罕见的,因为每次迭代都是为了解决上一代方案的真实攻击。
本文的核心问题不是”要不要 MFA”(答案是要),而是各因素的实际安全边界和工程落地成本。
一、认证因素的三种维度
“NIST SP 800-63B”(Digital Identity Guidelines, Authentication and Lifecycle Management)将认证因素分为三类:
| 因素类型 | 示例 | 攻击方式 |
|---|---|---|
| 你知道的(Knowledge) | 密码、PIN、安全问题答案 | 钓鱼、键盘记录、暴力破解、社工、撞库 |
| 你拥有的(Possession) | TOTP App、FIDO2 安全密钥、手机(接收 SMS) | TOTP 的实时钓鱼、SIM Swap(SMS)、物理盗窃 |
| 你本身的(Inherence) | 指纹、面部识别、虹膜 | 传感器欺骗(需要接触设备) |
MFA 的核心安全属性来自跨因素类型组合——两个”你知道的”(密码 + 安全问题答案)不等于 MFA,只是两个密码。密码 + TOTP 才是真正的 2FA。
二、TOTP:最普及也最有争议的第二因素
TOTP(Time-based One-Time Password,RFC 6238)是当前部署最广泛的第二因素。用户扫描 QR 码,每 30 秒生成 6 位数字——流程简单到几乎不需要培训。
2.1 TOTP 的内部实现
TOTP 基于 HMAC-based OTP(HOTP,RFC 4226),核心公式:
\[TOTP = HOTP(K, T)\]
\[HOTP(K, C) = \text{Truncate}(\text{HMAC-SHA-1}(K, C))\]
其中: - \(K\) 是共享密钥(secret)——在 QR 码扫描时传输的 Base32 编码字符串 - \(T\) 是时间步数:\(T = \lfloor (t_{current} - T_0) / T_X \rfloor\),默认 \(T_X = 30\) 秒 - Truncate 函数将 HMAC 的 160 位输出截取为 6-8 位十进制数字
服务端验证:用当前时间步(以及前后各一个步,共 3 个时间窗口,覆盖 90 秒内的有效值)计算期望的 OTP,与用户输入比对。
2.2 TOTP 的安全边界
TOTP 不能防御实时钓鱼(Real-time Phishing)。
攻击流程: 1.
攻击者搭建一个与真实登录页面完全相同外观的钓鱼网站。 2.
用户访问钓鱼网站,输入用户名和密码。 3.
钓鱼网站的代理服务器实时将凭据转发给真实的 IdP。 4. IdP 返回
TOTP 挑战。 5. 钓鱼网站展示 TOTP 输入框——用户输入 TOTP
验证码。 6. 钓鱼网站将验证码转发给真实 IdP。 7.
攻击者拿到完整会话——包括 Session Cookie 或
id_token。
整个过程中,用户以为自己登录了(因为攻击者在认证完成后将用户无缝跳转到真实网站),而攻击者在背后持有了完整的认证会话。TOTP 无法阻止这种攻击,因为 TOTP 验证码和密码一样——用户看到登录页面就输入,不管这个页面是不是真的。
这就是”Phishing-Resistant MFA”概念的核心动机。TOTP 不是 Phishing-Resistant 的。
2.3 TOTP 的种子安全管理
TOTP 的种子(Shared Secret)是安全关键数据。如果种子泄露,攻击者可以生成与用户完全同步的 TOTP 码。
- 种子在服务端存储时必须加密(AES-256-GCM 或等效),加密密钥从 KMS/HSM 读取。
- 种子在 QR 码传输中不暴露于 URL 参数(有些实现不小心把
secret=JBSWY3DPEHPK3PXP写在了 QR 码的附加 URL 里)。 - 支持种子重新生成——用户换手机时不应把旧种子迁移过去,而是重新注册 TOTP(旧种子在旧的 Authenticator App 中残留有安全隐患)。
三、WebAuthn / FIDO2:Phishing-Resistant 认证
WebAuthn(W3C Web Authentication, Level 3)和 CTAP(Client to Authenticator Protocol,FIDO Alliance)共同构成 FIDO2 标准,解决了 TOTP 的核心弱点——实时钓鱼。
3.1 WebAuthn 的密钥模型
WebAuthn 不是”一个密钥对”。每次向 RP 注册时,Authenticator(如 YubiKey、手机的安全芯片、平台 TPM)为该 RP 生成一个独立的公私钥对。所以它是”一个 RP,一个密钥对”——跨站点不可关联。
关键概念:
- Credential ID:公钥的唯一标识,由 Authenticator 生成,RP 在认证时用它来查找对应的公钥。
- RP ID(Relying Party ID):表示”这个
credential 属于哪个网站”。浏览器在 Authenticator
操作时强制验证 RP ID 与当前页面的 origin 一致。例如,RP ID
是
example.com,那么注册和认证操作只允许来自https://example.com或其子域https://login.example.com的页面。这是 WebAuthn 钓鱼防御的核心机制——即使攻击者搭建了视觉上完全相同的钓鱼页面,浏览器不会让 Authenticator 为evil.com生成对example.com的签名。 - User Handle:RP 内部用户 ID(不透明字节序列),由 RP 在注册时分配,认证时返回,帮助 RP 关联用户的多个 credential。
3.2 WebAuthn 注册与认证流程
sequenceDiagram
participant User as 用户 + 浏览器
participant RP as 应用 (RP)
participant Auth as Authenticator (YubiKey / 手机 TPM)
Note over User,Auth: === 注册 ===
RP->>User: 1. 发起注册 (challenge, rp.id, user.id, user.name, ...)
User->>Auth: 2. navigator.credentials.create(options)
Note over Auth: 3. 用户验证 (触摸/指纹/PIN)
Auth->>User: 4. 返回 Attestation + credentialId + 公钥
User->>RP: 5. POST /webauthn/register (响应的 JSON)
RP->>RP: 6. 存储 credentialId + 公钥, 关联 user.id
Note over User,Auth: === 认证 ===
RP->>User: 7. 发起认证 (challenge, allowCredentials: [credentialId1, credentialId2])
User->>Auth: 8. navigator.credentials.get(options)
Note over Auth: 9. 用户验证 (触摸/指纹/PIN)
Auth->>User: 10. 返回签名 (对 challenge + rpIdHash 等的签名)
User->>RP: 11. POST /webauthn/authenticate (响应的 JSON)
RP->>RP: 12. 用存储的公钥验证签名
关键安全点: -
challenge:RP
生成的随机值(至少 16 字节),服务端存储并与响应中的
clientDataJSON 中的 challenge 比对。防止重放。
- origin
校验:clientDataJSON 中的
origin 字段必须与页面源匹配,且包含在 RP ID
允许的范围内。这由浏览器填充,Authenticator 验证。 -
User Verification:Authenticator
可以配置是否要求”本地用户验证”(指纹、PIN
等)——提供了密码-less
认证的能力(注册时不需要密码,认证时靠生物识别或 PIN)。
3.3 Attestation 的工程含义
Attestation(证明)是 WebAuthn 注册时 Authenticator 提供的一个额外签名——它证明”这个公钥是由一个特定型号的、可信的 Authenticator 生成的”。Attestation 的工程价值在于风险引擎的决策——如果用户的 Authenticator 的 attestation 证书链验证通过,你可以确定”用户是在用什么等级的硬件在认证”。如果 attestation 失败但用户的 WebAuthn 签名正确,这通常是平台差异(不同浏览器的 Attestation 策略不同),不是攻击。
对于大多数 B2B SaaS,不需要强制要求 Attestation。WebAuthn 的核心安全保证(Phishing-Resistant)来自 rpIdHash + origin 绑定,不来自 Attestation。只有高安全需求场景(如金融交易确认、特权管理员登录)才需要。
四、Passkey:让 WebAuthn 跨设备可用
Passkey 是 FIDO 联盟对 WebAuthn credential 的消费化包装——解决了两个关键痛点:
- 跨设备同步:Passkey 通过平台账号(iCloud Keychain、Google Password Manager)在不同设备间同步,用户换手机不需要重新注册。
- 跨设备认证:手机扫码登录电脑——手机上的 Passkey 可以为电脑上的登录生成签名(通过蓝牙 + CTAP 2.1 caBLE 或 QR 码 + WebSocket)。
4.1 Passkey 的恢复流程
Passkey 的最大工程挑战不在认证时刻,在恢复时刻——用户换了手机、丢了手机、iCloud 账号被盗。
恢复策略: 1. 多 Passkey 注册:引导用户在初始设置时注册多个 Passkey(主手机 + 备用平板/YubiKey 安全密钥)。 2. 恢复码:生成一次性恢复码(如 16 位字母数字),用户保存后在丢失 Passkey 时输入,重建认证手段。 3. 平台恢复流程:Apple 和 Google 都有自己的账号恢复流程——如果用户的平台账号被盗,所有同步的 Passkey 将被泄露。这意味着 Passkey 的安全上限受限于用户平台账号的安全性。
工程陷阱:Passkey 的”The password killer”叙事在工程上需要被细化。Passkey 确实替代了密码在认证流程中的位置,但不替代密码在所有流程中的位置——尤其是账号恢复。一个”只支持 Passkey”的系统在用户丢失所有设备后,必须要么有”恢复密码”,要么有一个人工支持流程来重新建立身份的信任根。
五、五因素对比:成本、安全等级、用户流失
| 认证因素 | 钓鱼防御 | 部署成本 | 用户流失 | 适合场景 |
|---|---|---|---|---|
| SMS OTP | 弱(SIM Swap 攻击) | 极低(但按短信条数付费) | 极低 | 低价值 B2C,注册阶段的临时认证 |
| TOTP | 弱(实时钓鱼可绕过) | 低 | 低(设置需要 1-2 分钟) | B2B SaaS 的默认第二因素 |
| WebAuthn 安全密钥(YubiKey) | 强(硬件绑定) | 中(需要采购 + 邮寄) | 中(丢失后需要替换) | 高安全员工、管理者、运维 |
| Passkey | 强(origin 绑定 + 平台同步) | 低-中(需要最新系统/浏览器) | 极低(自动同步) | 面向消费者的现代认证 |
| 生物识别(单独使用) | 取决于实现——平台内嵌不可导出时为强 | 低 | 极低 | 移动端主认证(如银行 App 的指纹解锁) |
六、小结:MFA 策略的底线建议
- B2B SaaS 默认配置:密码 + TOTP,提供 WebAuthn/Passkey 作为高安全选项。
- B2C 默认配置:Passkey(+ 密码作为后备),逐步过渡到无密码。
- 员工内部系统:TOTP + WebAuthn 安全密钥(强制管理员和特权账户),有条件的话全部推 Passkey。
- 不要做:用 SMS 作为唯一第二因素——SIM Swap 攻击在 2024 年后大幅上升。SMS 可以作为”用户没有配置 TOTP”时的降级通道,但不是默认选项。
上一篇:Session、Refresh Token 与吊销体系 下一篇:风险感知认证:设备信任、异常登录与挑战升级
同主题继续阅读
把当前热点继续串成多页阅读,而不是停在单篇消费。
【身份与访问控制工程】IAM 全景:为什么这是高价值赛道
从 2020 年 SolarWinds 到 2024 年 Okta 支持系统泄露,身份基础设施的安全失败反复证明一件事:IAM 不是 IT 支撑系统,而是安全架构的承重墙。本文建立现代 IAM 的全景地图——从认证协议、令牌体系、权限模型到身份治理与平台选型,给出 5 个贯穿全系列的核心问题。
【身份与访问控制工程】SCIM 与账号生命周期:开通、变更、离职自动化
SSO 只解决认证,SCIM 解决账号的生命周期管理。但 SCIM 2.0 的实现远不是调几个 REST API 那么简单:User/Group schema 的映射、Delta vs Full sync 的同步策略、Patch 操作语义,每个环节都有坑。本文从账号生命周期的四个关键事件出发,拆解 SCIM 2.0 的核心协议、同步模式、Schema 扩展,以及与企业 IdP(Azure AD、Okta)对接的实际工程经验。
【身份与访问控制工程】企业单点登录:OIDC 与现代 SSO
OIDC 是当下企业 SSO 的事实标准,但大多数实现只用了它 20% 的规范。本文从 OIDC 核心规范出发,拆解 Authorization Code Flow + PKCE 的完整交互、ID Token 的验证规则、Discovery 与 Dynamic Registration 的互操作性机制,以及 RP-Initiated Logout 和 Session Management 的工程实现细节。
【身份与访问控制工程】OAuth 2.1 与 PKCE:现代授权主路径
OAuth 2.1 不是新协议,而是对 OAuth 2.0 的安全加固:废除 Implicit Grant 和 Resource Owner Password Grant,强制 PKCE 用于所有使用授权码模式的客户端,要求精确 redirect_uri 比对。本文从 PKCE 的密码学动机出发,拆解 OAuth 2.1 的授权码流程完整交互、Refresh Token 轮换与发送者约束、DPoP 令牌绑定,以及 DCR (Dynamic Client Registration) 和 RAR (Rich Authorization Requests) 的实际应用。