不少企业第一次认真对待短信验证码防刷,是某天早上打开后台,发现余额一夜之间少了一大截,发送记录里全是陌生号码,有的号码一晚上收到几十条。那时候才意识到,注册页上那个“获取验证码”按钮,谁都能点。
这篇写给负责登录注册和短信接口的开发、产品。先说结论:短信验证码防刷没有一招就灵的办法,要在发送前、发送中、发送后各设一道关。发送前用人机验证挡住脚本,发送中用频率限制控制数量,发送后靠监控尽早发现异常。下面只讲防护思路,具体阈值要结合自家业务的正常流量来定。
验证码接口被刷,损失在哪
最直接的是钱。每条短信验证码都要计费,脚本一晚上能把余额刷光,正常用户第二天反而收不到码,登录注册全卡住。
更麻烦的是短信轰炸。有人专门收集各个网站的验证码接口,拿来给某个手机号连续发短信,用来骚扰别人。被轰炸的人不知道背后是谁,只看到短信开头是你的签名,投诉自然冲着你来。投诉多了,签名和通道都可能被暂停,影响的是全部业务短信。
还有一种损失不太显眼:大量异常请求挤在一起时,正常用户的短信验证码可能到得更慢,等不及的人就直接关掉页面走了,这部分流失在报表里很难看出来。
第一道关:发送前做人机验证
图形验证码、滑块、点选文字,这些都是在判断“点按钮的是不是真人”。形式选哪种不太要紧,要紧的是两条规矩:验证结果必须在服务端校验,而且一次验证只能换一次发送机会。
我们见过不少项目只在前端判断按钮能不能点,倒计时也只做在页面上。可脚本根本不经过你的页面,直接请求接口,前端那些限制形同虚设。
体验上也要拿捏。每次都弹验证,正常用户会烦;比较常见的做法是平时简单验证,检测到异常请求时再加强。
第二道关:短信验证码的频率限制
频率限制要从几个维度同时做,只卡一个维度很容易被分散请求避开。
| 限制维度 | 主要防什么 | 要注意的地方 |
|---|---|---|
| 同一手机号 | 对单个号码反复发送、轰炸某个人 | 发送间隔和单日上限都要有 |
| 同一 IP | 一台机器批量请求 | 公司、学校共用出口 IP,别卡太死 |
| 同一设备 | 同一台设备换着号码请求 | 需要客户端配合提供设备标识 |
| 全站总量 | 大范围分散的请求 | 明显超出平时峰值就告警 |
计数逻辑放在服务端,用缓存做就行,开发量不大。上线前记得用测试号码把各种限制都触发一遍,确认短信验证码真的发不出去,而不只是页面上显示了提示。被限制时给用户的提示要客气,比如“操作太频繁,请稍后再试”,但别把具体规则写出来,“今天还剩几次”这种提示等于把底牌亮给对方。
在业务逻辑上少给机会
很多短信轰炸防护的漏洞,不在技术,在流程设计。下面几处花不了多少工夫,却能挡掉一大块风险:
- 有效期内用户重复点击,重发同一个码或提示稍后再试,不要每点一次都生成新码;
- 短信验证码输错次数设上限,超过就作废,重新获取;
- 只在真正需要验证的步骤发码,别页面一打开就自动发;
- 业务不涉及海外用户,就在平台关掉国际短信,少一个被利用的口子。
发送后:监控比规则更重要
规则总有考虑不到的地方,所以要能尽快发现异常。几个典型信号:半夜发送量突然上涨;接收号码集中在某些号段;短信验证码发出去很多,真正完成验证的却很少,码发了没人用,基本可以判定是被刷。
监控要接告警,推到值班人员的手机上,别只躺在报表里。短信平台那边也建议设好单日发送上限和余额预警,作为最后一道保险。确认是恶意来源的 IP 和设备,加进黑名单。
已经被刷了,按这个顺序处理
- 先止损:临时收紧频率限制,必要时暂停验证码入口,换成其他登录方式过渡;
- 在短信平台后台调低发送上限,或联系平台协助处理;
- 导出发送日志,看请求来自哪些 IP、设备和号段,找到漏洞;
- 补上漏洞后逐步放开,同时盯着发送量曲线;
- 对被轰炸的号码停止发送,有用户来电投诉的,如实说明并致歉。
结论
短信验证码防刷要分层:发送前做服务端校验的人机验证,发送中从手机号、IP、设备和总量几个维度限频,业务流程上不给无限重发的机会,发送后靠监控告警和平台上限兜底。被刷时先止损再查原因。正在用或准备接入短信群发接口、想把验证码流程的防护梳理一遍,可以打 18720919319,和蔚岚科技的技术同事一起过一遍。
常见问题
加了图形验证码,还会被刷吗?
有可能。单靠一道人机验证不够,要和频率限制、监控告警一起用,任何一层被突破,后面还有拦挡。
同一个号码多久能重新获取一次短信验证码?
常见做法是间隔一分钟左右,同时设单日上限。具体按业务场景调,登录频繁的产品可以适当放宽。
被刷掉的短信费用能追回吗?
一般很难。平台通常按实际提交发送的条数计费,所以防护要做在前面,具体情况可以和平台沟通。
短信轰炸防护要不要单独买风控产品?
中小网站把人机验证、限频和监控做好,通常够用。流量大、经常被盯上的业务,可以再考虑专业的风控服务。
小程序和App里的短信验证码也要做防刷吗?
要做。客户端上的限制挡不住直接请求接口,所有校验和计数都必须放在服务端。