
什么是双因素认证(2FA)?
双因素认证(2FA) 在密码之外增加了第二层验证。即使攻击者窃取了你的密码,如果没有第二因素,他们也无法登录。三种认证因素类别如下:
- 你知道的信息 — 密码、PIN、安全问题
- 你拥有的物品 — 手机、硬件令牌、智能卡
- 你的生物特征 — 指纹、面部识别、声音
大多数 2FA 实现将密码(你知道的信息)与来自移动应用的一次性代码(你拥有的物品)结合使用。

TOTP 的工作原理
TOTP(基于时间的一次性密码)在 RFC 6238 中定义。它生成一个每 30 秒变化一次的 6 位数字代码,在应用和服务器之间无需任何网络通信即可同步。
设置过程:
- 服务器生成一个随机的密钥(通常为 20 字节 / 160 位)。
- 服务器将密钥显示为二维码。
- 用户使用身份验证器应用(Google Authenticator、Authy 等)扫描二维码。
- 服务器和应用现在共享相同的密钥。
代码生成:
counter = floor(currentUnixTime / 30)
HOTP = HMAC-SHA1(secret, counter)
TOTP = HOTP truncated to 6 digits
由于双方都根据相同的计数器(当前时间 ÷ 30)和相同的密钥计算 TOTP,因此它们总是得到相同的 6 位代码——无需网络。
验证:
服务器检查提交的代码是否与当前 TOTP 匹配,通常还会检查前一个和下一个(以考虑时钟偏差和网络延迟)。有效代码被接受一次,然后失效以防止重放攻击。

HOTP:基于计数器的前身
HOTP(基于 HMAC 的一次性密码,RFC 4226)使用递增计数器而非时间。每次生成代码时计数器递增。
- 优点: 无需时钟同步,适用于硬件令牌。
- 缺点: 如果生成了代码但未使用,计数器可能不同步。
TOTP 通常更受软件身份验证器的青睐。
身份验证器应用
| 应用 | 开源 | 多设备 | 备份 |
|---|---|---|---|
| Google Authenticator | ❌ | iOS + Android | 云备份选项 |
| Authy | ❌ | 是 | 加密云备份 |
| Microsoft Authenticator | ❌ | 是 | 云备份 |
| Aegis | ✅ | Android | 加密文件备份 |
| Raivo | ✅ | iOS | iCloud 备份 |
为了最大安全性,推荐使用 Aegis(Android)和 Raivo(iOS)——两者都是开源的,并支持加密的本地/云备份。
TOTP 安全特性
抗钓鱼
TOTP 代码仅在 30 秒内有效。如果用户被诱骗在钓鱼网站上输入代码,攻击者必须在代码过期前使用它——而且只能使用一次。

防重放
由于每个代码都有时间限制且一次性使用,重放截获的代码将无效。
抗暴力破解
在 10^6 种可能的 6 位代码和 30 秒有效期内,攻击者在窗口过期前每秒只能检查 3 个代码。锁定策略进一步限制了尝试次数。
TOTP 的局限性
- SIM 卡交换 — 基于短信的 2FA 易受攻击;TOTP(基于应用)则不受影响。
- 实时钓鱼 — 高级攻击实时代理凭据,绕过 TOTP。硬件密钥(FIDO2/WebAuthn)可防止此类攻击。
- 设备丢失 — 丢失手机且没有备份代码会导致无法登录。请始终保存备份代码。
- 账户恢复 — 许多服务的账户恢复机制薄弱,完全绕过 2FA。
在应用中实现 TOTP
// 使用 'otplib' (npm)
import { authenticator } from 'otplib';
// 生成密钥(仅在用户设置时执行一次)
const secret = authenticator.generateSecret();
// 生成当前 TOTP
const token = authenticator.generate(secret);
// 验证用户提交的令牌
const isValid = authenticator.verify({ token, secret });
# 使用 'pyotp' (pip)
import pyotp
secret = pyotp.random_base32()
totp = pyotp.TOTP(secret)
token = totp.now() # 当前代码
totp.verify(token) # 返回 True
使用此工具
此 OTP 生成器接受 TOTP 密钥(Base32 编码),并显示当前代码以及倒计时器。你还可以根据密钥验证代码——这对于测试你的 TOTP 实现非常有用。
→ 尝试 OTP 生成器