记一次短信验证码服务异常排查:从通道稳定性想到的多因素认证设计

去年秋天,我在做一个面向海外用户的SaaS项目,注册流程需要短信验证码做身份校验。+86手机号作为测试账号,验证码要么延迟十分钟以上,要么干脆收不到。
这个问题在开发和测试环境没出现过,到了预发布环境才暴露。排查了三天,最后发现不是代码bug,是通道本身的稳定性问题。
第一天:排除代码层面的问题
项目用的是.NET 6,短信服务封装了一个HttpClient调用第三方API。本地调试时,+1(美国)和+44(英国)的号码都能正常收到验证码,平均延迟3秒。
但换成+86号码后,现象很奇怪:
请求返回200,状态显示"已发送"
实际手机收不到短信
偶尔能收到,但延迟超过10分钟,验证码已过期
我第一反应是HttpClient配置有问题。检查了超时设置、DNS解析、TLS版本,都没异常。又怀疑是第三方SDK的bug,降级了版本,问题依旧。
第一天结束时,基本排除了代码和SDK的问题。
第二天:对比不同运营商和通道
第二天做了更系统的测试:
电信卡:发送10次,收到2次,平均延迟15分钟
联通卡:发送10次,收到5次,平均延迟8分钟
移动卡:发送10次,收到1次,延迟超过30分钟
同时发现,同一个号码重复发送时,第二次会提示需要支付额外费用(smsfee),但支付后依然收不到。
这时候我意识到,问题不在我们的系统,在短信通道本身。+86号码走的是国际短信通道,近期这条通道的稳定性明显下降。不是技术问题,是基础设施层面的限制。
第三天:思考替代方案和架构改进
既然短信通道不可靠,有没有其他验证方式?
我调研了几种替代方案:
方案一:邮件验证
实现简单,SMTP现成的
但国内用户邮箱使用率不高,且邮件进垃圾箱的概率大
作为辅助可以,不能替代
方案二:第三方OAuth授权
微信、GitHub、Google等
但我们的用户群体分散,不是所有用户都有这些账号
且OAuth引入额外的依赖和隐私合规问题
方案三:基于时间同步的软令牌(TOTP)
类似Google Authenticator的实现
不依赖任何通道,完全离线
但用户教育成本高,且需要额外安装App
最终采用的架构
没有选单一方案,而是做了分层设计:
主通道:短信验证码,覆盖大部分用户
备用通道:邮件验证,短信失败时自动切换
兜底方案:TOTP软令牌,对安全要求高的用户可选
通道健康检查:每5分钟检测各通道成功率,低于阈值自动告警并切换
代码层面,用Polly做了重试和熔断:
短信发送失败,重试2次
连续5次失败,熔断该通道10分钟,切换到备用
熔断期间,新请求直接走备用通道
这次排查的收获
以前做系统架构,我默认"短信通道是可靠的",把短信作为唯一验证方式。这次经历让我意识到,任何外部依赖都可能失效,设计时要考虑降级和兜底。
特别是面向全球用户的产品,不同地区的网络环境、运营商策略、政策法规差异很大。不能用一个假设覆盖所有场景。
几个设计原则:
多通道并行:不要依赖单一验证方式
自动降级:主通道失败时,用户无感知切换
健康监控:实时知道哪个通道有问题,而不是等用户投诉
失败透明:告诉用户"当前通道繁忙,已切换备用方式",而不是"请稍后再试"
最后的问题
你们在做多因素认证或注册流程时,有没有遇到过"某个验证通道在特定地区完全不可用"的情况?最后是怎么处理的?是硬等通道恢复,还是做了备用方案?
我目前的设计是Polly做熔断切换,但TOTP的用户教育成本还没找到好的解决办法。有经验的朋友欢迎分享。
我分享在评论区了

浙公网安备 33010602011771号