随着手机的普及,手机验证码登录需求已经成为一个很常见的需求,但是这么一个看似简单的需求,其实还是有很多坑的。
昨天使用兄弟团队的登录界面,就发现了一些安全问题,在这边整理了一些我的经验和坑点,写下来备忘和参考。

1、所有的数据存储和验证,一定要在服务端处理。

这点只要做过一段时间Web开发的,都理解:

  • 前端的数据、加密算法、密钥都是公开的,很容易泄露。
  • 前端的验证,都是可以绕过的,只能作为用户体验优化方案,服务端都必须做验证。

我见过的反面案例:

  • 后端生成验证码,明文或经过加密后返回给前端,前端在js里判断输入的验证码是否正确

2、手机号格式校验

用户输入的手机号是不是合法的格式,鉴于现在手机号段,都是1开头,第2位除了0,1和2,其它都有,我常用的正则是: ^1[3-9]\d{9}$ 注1:网上找到了号段分布:
一、中国电信号段133、149、153、173、177、180、181、189、190、191、193、199
二、中国联通号段130、131、132、145、155、156、166、167、171、175、176、185、186、196
三、中国移动号段134(0-8)、135、136、137、138、139、1440、147、148、150、151、152、157、158、159、172、178、182、183、184、187、188、195、197、198
注2:服务端一定要校验手机号格式是否合法,但是前端也建议加上,增强用户体验。

我的案例,号段变化太快,导致校验规则代码不断调整:
以前只有13x号段,后面增加15x、18x,再后面又增加17x、19x,又增加14x、16x。
具体号段发展历史参考:https://www.cesc.com.cn/quote/show-358652.html

3、短信发送频率控制

如果不做频率控制,那么恶意用户可能无限发送短信,把你的接口作为短信轰炸机,轰炸别人,造成你的成本浪费,甚至可能被工信部调查。
常见作法(注:以下步骤,建议全部实现):

  • 根据用户IP,限制发送次数,比如 一个IP一分钟内只能发一次短信
  • 根据用户手机号,限制发送次数,比如 同一个手机号一分钟内只能发一次短信
  • 同一个IP 或 同一个手机号,一天内发送超过3条短信时,增加图形验证码或滑块等机制
  • 同一个手机号,一天内发送超过20条短信时,加入黑名单,禁止调用
  • 同一个IP,一天内发送超过100条短信时,加入黑名单,禁止调用

可能导致的问题点:

  • 一个公司或一个区域的用户,可能是同一个出口IP,导致有些人互相影响
  • 在不同产品之间同时登录,是否允许同时发短信,此时Redis的key可以加上产品标识

我见过的反面案例:

  • 之前百度某项目,修改个人资料需要短信验证,没做频率控制,造成一天发了400万条短信
  • 前一公司登录页,使用Session或Cookie,进行发短信的频率控制,在代码评审时发现。
    我戏称:这段代码只限制了公司的前端团队,没有限制恶意用户。

4、验证码生成规则

基于用户体验,现在生成的手机验证码,90%以上都是随机的全数字,有4位,有6位。
基于安全考虑,建议使用随机生成的6位数字,同时应当避免生成极简单的验证码,减少碰撞概率。
注:应当使用一个静态的Random变量,运行过程中new的变量可能生成相同的验证码。

应当避免生成的验证码规则:

  • 相同的数字,如 000000 111111
  • 连续的数字,如 123456 67890

我见过的反面案例:

  • 曾经收到过 222222 这种验证码,如果恶意用户输入了222222,就进去了

注:复杂验证码也有碰撞概率,但是相对简单数字,概率比较小

5、验证码失效规则

为了避免暴力破解,对验证码应该有验证次数、验证时效的限制:

  • 登录成功后,该验证码应当立即失效,不允许再次使用
  • 验证码校验错误5次,该验证码应当立即失效,不允许再次使用
    注:1次也可以,会多点钱,用户体验也差一点
  • 验证码在180秒内有效,超时后该验证码应当立即失效,不允许使用
  • 发送验证码成功时,所有的验证码应当全部失效,不允许使用
    注:最简单作法,根据手机号,查找最后一条验证码(不管状态,最后一条是失效的也要获取)

我见过的反面案例:

  • 上文写的兄弟团队的登录页面,就没有对错误次数做限制,6位数字,最坏情况下999999次登录,一定可以登录成功。
  • 验证码成功后不失效,导致退出后,还可以使用上一次成功的验证码

6、验证码关联规则

6.1、验证码必须跟手机关联,即:服务端要根据手机号,查找最近一条验证码,避免其它手机号的验证码被使用;
6.2、存在多项目时,验证码必须跟项目或顺序关联:
为避免多项目间的验证码串扰,或同一项目发了多次验证码时,使用哪一个验证码有效的问题,生成的验证码应该配对一个序列号,用户输入验证码,点登录时,把序列号一起带给服务端进行校验。

7、其它

加上了上面的限制,依然不可避免被利用,比如市面上的短信轰炸机,就是收集了网上几乎所有的短信验证接口,它只需要每个接口调用一次就够了,什么新浪网易淘宝,都被短信轰炸机收录了。
这种恶意应用,几乎无法避免,只能不断修改发送规则,举几个例子:

  • 进入登录页,服务端生成一个token给前端,前端发短信必须带上这个token,没有token或token无效不让发短信
  • 进入登录页,调用一个接口,起个奇怪的名字,比如 addlog,其实是在服务端生成一个Session,发短信时,服务端不存在Session,报一个服务器失败,不给用户明确的提示
  • 对上面这些非法请求的IP,加入黑名单

8、短信登录流程图

短信登录一定用redis吗 短信登录安全吗_验证码