臭名昭著的手机验证码功能是如何实现的

前言

如今基本上各类手机APP注册都会用到手机验证码,包括一些PC端网站也会使用手机号做为惟一标识验证!前端

恰巧,小明的老板,让其开发一个用户注册的功能,而且强制用户注册绑定手机,美其名曰为了提高安全性,呵呵哒,就是为了多撸一点用户信息。java

案例

通常来讲,发送手机验证码不能过于频繁,前端发送按钮点击后通常会有一个60秒倒计时的功能。也就是说,若是用户点击发送一直没有收到验证码,只能60秒以后才能够进行重发。ajax

那么问题来了,若是用户绕过前端,直接向后台API发送短信请求,而后写个无限循环脚本,相信不久你的短信帐户就会发来预警提示短信(通常来讲大的短信商都有预警设置功能)。json

其实很简单,你只须要F12,查看发送请求就能够查找出后台请求地址,而后你能够在控制台输入相关JS代码,执行个十万遍,是否是很爽?后端

这里以七牛云为测试案例,打开注册页面,F12进入调试模式,输入手机号,手动点击发送,获取其短信发送后台请求地址。下面是七牛云的一个短信发送请求,撸主测试了一下,显然没有达到撸主的预期,毕竟是大厂,防护措施仍是作的很牛逼的。api

如下是JS脚本,复制粘贴到控制台回车就能够执行:安全

var data = {"operation":1,"is_voice":false,"mobile_number":"17762018888","captcha_type":2};for (var i = 0; i < 10; i++) {
    $.ajax({        type: 'POST',		contentType: 'application/json;charset=UTF-8',        data:JSON.stringify(data),        url: 'https://portal.qiniu.com/api/gaea/verification/sms/send',        success: function(data) {            console.log(data)
        }
    });
}

控制台返回如下信息,前三次请求成功,后面的就出现了验证码校验并进行了限流操做。session

{"code":200,"message":""}
{"code":200,"message":""}
{"code":200,"message":""}
{"code": 7209,"message":"captcha required"}
{"code": 7209,"message":"captcha required"}
{"code": 429,"message":"too many requests"}
{"code": 429,"message":"too many requests"}
{"code": 429,"message":"too many requests"}
{"code": 429,"message":"too many requests"}
{"code": 7209,"message":"captcha required"}

撸主尝试刷新页面,随便输了一个手机号,再次点击发送,提示用户输入验证码,显然是增强了防备,触发了恶意请求认证拦截机制。app

安全机制

对于开发者来讲,他们不只要考虑用户正常获取验证码的体验还要考虑短信接口的安全性,撸主总结了如下几点,但愿对你们有所帮助。dom

  • 后台请求限流,对单位时间内发送频率作限制。

  • 验证码机制,切记不要一开始就限制验证码,体验及其不友好,触发限流之后开启验证码校验。

  • 监控日发送短信数量,触发必定的阈值作相应的处理,根据实际业务需求。

  • 验证码存储必定要保证key为手机号,切记不要以其它标识做为key,好比sessionId

  • 必定要设置验证码失效时间,好比五分钟,或者更短。

  • 验证码尽可能保证短小精悍,四到六位便可。

  • 若是后台不作限制,切记前台必定要作个倒计时的限制,至少过滤一部分小白用户。

代码案例

给小伙伴分享一个简单的验证码生成、存储、失效代码案例:

import com.google.common.cache.CacheBuilder;import com.google.common.cache.CacheLoader;import com.google.common.cache.LoadingCache;import java.util.concurrent.ExecutionException;import java.util.concurrent.TimeUnit;public class Mobile {    /**
     * 测试方便,这里设置了3秒失效
     */
    private static LoadingCache<String, String> caches = CacheBuilder.newBuilder()
            .maximumSize(1000)
            .expireAfterWrite(3, TimeUnit.SECONDS)
            .build(new CacheLoader<String, String>() {                @Override
                public String load(String mobile) {                    return "";
                }
            });    public static void main(String[] args) throws ExecutionException, InterruptedException {
        Integer code = (int)((Math.random()*9+1)*100000);
        caches.put("17762018888",code.toString());
        System.out.println(caches.get("17762018888"));
        Thread.sleep(4000);
        System.out.println("是否是没了:"+caches.get("17762018888"));
    }
}

小结

重要的功能必须进行先后端校验,必要的时候必定要作好限流、黑名单等骚操做!!!

相关文章
相关标签/搜索