正在加载,请稍候…

一次性密码:OTP 的工作原理与安全实现要点

深入理解 HOTP 和 TOTP 的密码学原理、常见实现陷阱,以及如何正确使用 OTP 生成器进行多因素认证。

一次性密码(OTP)是现代身份认证的基石,广泛应用于登录、密码重置和交易验证。但并非所有 OTP 都同等安全,糟糕的实现可能将安全特性变成漏洞。本文解释 HOTP 和 TOTP 的密码学原理,指出常见陷阱,并展示如何有效使用 OTP 生成器。

显示安全应用界面上 6 位 OTP 码的智能手机

什么是 OTP?

一次性密码(OTP) 是一个临时、一次性使用的代码,用于验证特定会话或交易中的用户身份。与静态密码不同,OTP 快速过期且不可重复使用,因此能抵抗重放攻击。

OTP 通常通过短信、电子邮件或认证器应用传递。但其底层算法——HOTP(基于 HMAC 的一次性密码)TOTP(基于时间的一次性密码)——定义了代码的生成方式。

HOTP 与 TOTP:核心算法

HOTP(RFC 4226)

HOTP 使用一个计数器,每次成功认证后递增。服务器和客户端共享一个密钥,两者计算 OTP 的方式为:

HOTP(K, C) = Truncate(HMAC-SHA-1(K, C))

其中:

  • K 是共享密钥。
  • C 是一个 8 字节的计数器值。
  • Truncate 提取动态二进制码并转换为可配置长度的十进制 OTP(通常 6-8 位)。

优点: 可离线工作;无需时钟同步。 缺点: 如果用户生成代码但未认证(例如多次点击“下一个”),计数器会不同步。

TOTP(RFC 6238)

TOTP 将计数器替换为时间步长(通常 30 秒)。代码计算方式为:

TOTP(K, T) = HOTP(K, floor((current_time - T0) / X))

其中:

  • T0 是 Unix 纪元(0)或自定义起始时间。
  • X 是时间步长(例如 30 秒)。

优点: 自动重新同步;无计数器漂移。 缺点: 需要精确时钟;容易受时钟偏差影响(通常允许 ±1 步长)。

特性 HOTP TOTP
输入 计数器(递增) 时间(当前 Unix 时间)
同步方式 手动(计数器重同步) 自动(基于时间)
时钟要求 需要精确时钟
常见用途 硬件令牌 认证器应用

为什么 OTP 实现很重要?

一个实现良好的 OTP 系统能提供强大的多因素认证(MFA)。然而,常见错误可能使其不安全。研究(例如 OTP-Hunter,2025)已识别出四大漏洞类别:

  1. OTP 轰炸——向目标重复发送 OTP,造成骚扰或资源耗尽。
  2. OTP 暴力破解——由于缺少速率限制,枚举短 OTP 码(例如 4-6 位)。
  3. OTP 泄漏——在日志、URL 或 API 响应中暴露 OTP。
  4. 资源消耗——通过大量 OTP 生成请求导致服务器端拒绝服务。

SIM 卡劫持威胁

基于短信的 OTP 特别容易受到 SIM 卡劫持攻击,攻击者诱骗运营商将受害者的手机号码转移到新 SIM 卡。一旦控制,攻击者就能收到所有短信 OTP。这已促使菲律宾央行(BSP)等监管机构强制要求在 2026 年前淘汰短信 OTP。

常见实现陷阱

  • 未对 OTP 请求进行速率限制——攻击者可淹没服务器,导致拒绝服务或暴力破解尝试。
  • OTP 长度过短——4 位代码只有 10,000 种组合;若无速率限制,可在几分钟内暴力破解。
  • 可预测的 OTP 种子——使用弱随机数生成器(例如 rand() 而非 crypto/rand)使密钥可被猜测。
  • 以明文存储 OTP——数据库泄露会暴露活动代码。
  • 忽略时钟偏差——TOTP 实现必须允许一个窗口(例如 ±1 时间步长)来应对时钟漂移,但窗口过大会削弱安全性。
  • 无账户锁定——几次失败尝试后,应临时锁定账户以防止暴力破解。

完整示例:生成并验证 TOTP

让我们用 Python 的 pyotp 库走一遍完整示例。

第 1 步:安装库

pip install pyotp

第 2 步:生成密钥

import pyotp

# 生成随机 base32 密钥(16 字符 = 80 位)
secret = pyotp.random_base32()
print(f"Secret: {secret}")

示例输出:JBSWY3DPEHPK3PXP

第 3 步:创建 TOTP 对象并获取当前代码

totp = pyotp.TOTP(secret)
current_code = totp.now()
print(f"Current OTP: {current_code}")

第 4 步:验证代码

# 模拟用户输入
user_input = input("Enter OTP: ")

if totp.verify(user_input):
    print("Valid OTP")
else:
    print("Invalid OTP")

第 5 步:为认证器应用生成二维码

uri = totp.provisioning_uri(name="user@example.com", issuer_name="MyApp")
print(f"URI: {uri}")
# 使用二维码库(例如 qrcode)将此 URI 显示为二维码。

URI 遵循 otpauth:// 方案,认证器应用(Google Authenticator、Authy)可扫描。

使用 OTP 生成器工具

我们的 OTP 生成器 让你快速生成 HOTP/TOTP 代码用于测试或演示。你可以指定密钥、算法、位数和时间步长。这对于以下场景特别有用:

  • 调试应用中的 OTP 集成。
  • 无需搭建完整服务器即可生成测试代码。
  • 理解不同参数如何影响输出。

安全最佳实践

  1. 大多数应用使用 TOTP 而非 HOTP——时间同步比计数器管理更容易。
  2. 对 OTP 生成和验证端点实施速率限制
  3. 至少使用 6 位代码(推荐 8 位)。
  4. 设置较短的 OTP 有效期(例如 TOTP 为 30 秒,邮件 OTP 为 5 分钟)。
  5. 切勿以明文记录 OTP
  6. 使用密码学安全随机数生成密钥
  7. 在 3-5 次失败尝试后实施账户锁定
  8. 考虑基于推送的认证(例如应用内 OTP)以避免 SIM 卡劫持风险。

常见问题

HOTP 和 TOTP 有什么区别?

HOTP 使用每次递增的计数器,而 TOTP 使用当前时间作为输入。TOTP 在认证器应用中更常见,因为它无需用户操作即可自动同步。

OTP 能被暴力破解吗?

可以,如果没有速率限制。6 位代码有 100 万种组合;以每秒 1000 次请求计算,约 16 分钟可破解。速率限制和账户锁定至关重要。

为什么短信 OTP 被认为不安全?

短信 OTP 容易受到 SIM 卡劫持、SS7 拦截和钓鱼攻击。监管机构正越来越多地强制要求使用应用内 OTP 或生物识别等替代方案。

OTP 的有效期应该多长?

对于 TOTP,30 秒是标准。对于邮件或短信,通常 5-10 分钟。更长的窗口会增加拦截风险。

OTP 模糊测试中的“云触发器”是什么?

云触发器是应用代码中最后一个参数(如手机号、OTP)仍未被加密或签名的函数。在此处进行模糊测试可以绕过加密和完整性检查,从而发现漏洞。