OTP 与过期

Redis OTP 与验证码过期

构建安全的、具有自动过期功能的 Redis 一次性密码 (OTP) 和验证码系统。非常适合短信验证、邮箱确认和 2FA。

真实业务场景

某金融科技 App 需要短信验证才能登录和交易。每个 OTP 必须精确有效 5 分钟,限制尝试验证 3 次,且用户每 60 秒只能请求 1 个 OTP。在 SQL 数据库中存储需要复杂的清理作业。Redis TTL 自动处理过期,原子操作确保在无竞态条件的情况下执行安全约束。

架构图

用户请求 OTP → 应用服务器
生成 6 位代码
┌─────────────────────────────────────┐
│ Redis │
│ ┌─────────────────────────────────┐ │
│ │ otp:{phone}:code = 583921 │ │
│ │ TTL = 300s (5 分钟) │ │
│ ├─────────────────────────────────┤ │
│ │ otp:{phone}:attempts = 0 │ │
│ │ TTL = 300s │ │
│ ├─────────────────────────────────┤ │
│ │ otp:{phone}:cooldown = 1 │ │
│ │ TTL = 60s (重发冷却) │ │
│ └─────────────────────────────────┘ │
└─────────────────────────────────────┘
↓ 通过短信网关发送
用户收到代码 → 提交验证

关键命令解析

性能分析

带 EX 和 NX 的 SET: O(1), ~0.1ms。无需后台清理作业——Redis TTL 自动处理过期。
内存: 每个 OTP 使用约 3 个 key × ~100 bytes = ~300 bytes。100万并发 OTP = ~300MB。非常易于管理。
INCR 用于尝试计数: O(1),原子性。即使在并发验证请求下也没有竞态条件。
TTL 精度: Redis 以毫秒级精度过期 key。没有在 5:01 提供 OTP 的风险。

常见陷阱

SET 未使用 NX: 如果没有 NX,第二个 OTP 请求会覆盖第一个,导致已经收到原始代码的用户困惑。
忘记尝试限制: 如果没有基于 INCR 的尝试跟踪,攻击者可以在几分钟内暴力破解 6 位 OTP(100万种组合)。
以明文存储 OTP: 对于高安全性场景,应存储 OTP 的哈希值 (SHA-256)。在验证时比较哈希值。
请求之间无冷却: 如果没有冷却 key,自动化脚本可以触发数千条短信,造成资金损失并骚扰用户。

最佳实践

始终配合使用 SET NX EX——在一个命令中完成原子创建和 TTL 设置。
限制尝试次数为 3-5 次,并在超出限制时删除 OTP。
实施重发冷却 (60s) 以防止短信轰炸。
使用加密安全的随机数生成器生成 OTP 代码。
记录所有 OTP 生成和验证事件以作为审计跟踪。
对于更高安全性的需求,考虑使用基于 HMAC 的 OTP (HOTP/TOTP) 进行 2FA,而不是短信。

可运行演示

Redis Demo
Click "Step" or "Run All" to execute commands...

在我们的 Redis 在线编辑器中尝试这些命令