🔐 OTP 不是身份验证——它是一种代价高昂的副作用:探索 OTPshield
Source: Dev.to

对 OTP 的常见误解
当开发者通过短信实现 OTP 时,通常的思维模型是:
“如果用户请求 OTP,我们就发送一个。”
这种假设隐藏了两个危险的想法:
- 每个请求都是合法的
- 发送的成本可以忽略不计
在大规模使用时,这两个假设都会失效。
OTP 作为一种有代价的副作用
每一次 OTP 请求:
- 📲 触发一次付费短信
- 💳 产生外部成本
- ⚙️ 自动执行
从攻击者的角度来看,这正是完美的:
- 不需要身份验证
- 不需要提升权限
- 不需要利用漏洞
只要不断重复即可。
为什么攻击者喜欢 OTP 接口
- 🌍 天生公开
- ⚡ 易于自动化
- 🔁 易于重放
- 💰 对防御方成本高
攻击者不追求成功率,只需要大量请求。OTP 滥用之所以有利可图,是因为失败并不重要。
“我们已经有 CAPTCHA 和速率限制了……”
很多团队的回应是:
- CAPTCHA
- IP 速率限制
- 重试次数限制
这些措施有帮助——但只能部分缓解。
- ❌ CAPTCHA 可以被绕过或外包
- ❌ 速率限制难以抵御分布式 bot
- ❌ 手机号码的更换速度快于 IP
结果: 仍然会发送过多的 OTP。
缺失的概念:将 OTP 视为特权
架构转变
🔑 请求 OTP 应被视为一种 特权,而不是理所当然的权利。
在发放 OTP 之前,系统应先询问:
- 谁在请求?
- 这个请求是否典型?
- 这个号码看起来是否合法?
- 成本是否合理?
重新构建 OTP 流程
传统流程
Request OTP → Send SMS → Verify code
改进后流程
Request OTP
→ Risk evaluation
→ Decision
→ Send SMS (only if justified)
一个决策点即可改变一切。
发送短信前可用的信号
即使没有用户身份验证,也可以分析:
- 📱 手机号码类型(真实移动号码 vs. VOIP)
- 🕒 请求频率和模式
- 🌍 地理位置一致性
- 📊 滥用历史和信誉
这些都不需要实际发送短信。
最小化的条件式 OTP 逻辑
if risk_score < threshold:
send_sms_otp()
else:
deny_or_challenge()
OTP 的发送变成了有条件的。
示例实现
我们使用了上游风险分析 API(OTPShield)在调用任何短信提供商之前先评估电话号码。这让我们能够:
- 阻止大多数滥用请求
- 为真实用户保留良好体验
- 大幅降低短信费用
无需修改 OTP 的代码逻辑——只需更好的入口把关。
这种转变后会有什么变化
- 📉 短信发送量减少
- 🔐 攻击面减少
- 💰 账单更可预测
- 🧠 安全决策更贴近业务意图
最重要的是:你不再为攻击者的测试付费。
谁需要关注此事
如果你的业务涉及:
- 面向消费者的应用
- 基于 OTP 的登录或注册
- 全球手机号支持
- 非常规的短信费用
即使对你来说 OTP “很便宜”,攻击者也可能已经注意到了。
最终要点
OTP 不是 身份验证。它是一种带有费用标签的副作用。
把 OTP 当作 有条件的操作 而不是默认响应,你的安全姿态和成本结构会立刻得到改善。