验证码防爆破问题的引发的限流技术思考

最近在工作中被扫出来一个工单,大致意思是手机短信验证码登录的逻辑没有添加限制的逻辑,会有被爆破的风险。(截图仅为举例,无实际意义)
在这里插入图片描述
在这里插入图片描述

具体操作流程如下:

  1. 用户在登录界面填写手机号,不获取短信验证码,直接通过爆破模块(burp suite),生成一堆的验证码,直接脚本批量尝试登录。
  2. 重置密码的逻辑,同样也可以通过这种爆破验证码的逻辑,跳过短信验证码,直接修改密码。

风险:

  • 只需要知道用户的手机号,就可以短信登录,或者修改密码。

工程已经做的措施:

  1. 发送验证码保护:图形拖拽验证,发送验证码按钮限频率,防机器人操作,减少手动爆破的可能性。

  2. 验证码发送频率限制:防止用户通过验证码功能,对他人手机进行短信轰炸。

防爆破问题后台解决方案:

  1. 验证码设置失效时时间(如:1分钟),申请新的验证码,旧验证码也得过期,原理:缩短验证码有效时间,降低爆破模块在有效时间内尝试到有效的验证码的可能性
  2. 限制账户认证失败次数,失败次数太多后,锁定账户,防止机器人操作。
  3. 风控: 通过识别deviceID , IP 等手段, 识别黑产设备,拉黑设备。

OK, 问题,风险和解决方案我们都分析了。其实上面的解决方案大家很容易就能想到,但是本文的重点确不是解决这个问题。而是从问题入手引发我们的思考。接下来我们主要是针对方案二来引申思考。眼尖的同学肯定已经看出来方案二说白了就是限流的问题模型。

限流:后台服务中常用到的一种保证服务高可用的手段。

我们了解限流,总是要从全面的了解他,为什么要限流?从哪些方面限流?怎么限流?

1.为什么要限流?

现在我们后台服务设计的时候都要求要保证三高(高性能,高并发,高可用),毕竟我们的服务进程和服务器本身的处理能力都是有限的,超出了处理能力之后,服务进程和服务器都有可能崩溃。甚至会级联其他服务导致雪崩 (当然防雪崩的主要手段是从调用方对下游服务进行熔断,这是另一个话题,我们后续在聊),而限流是一种服务自身为了保护自身可用性的一种保护手段,一般需要服务自身实现。

2.从哪些方面限流?

聊限流的方式方法之前,我们先要明白从哪些方面限流。也就是说我们尝试使用限流的手段来保证服务可用性的时候,需要考虑那些方面。

先说答案:从哪些方面?架构,服务,接口。

我们模拟一个对话场景:

Q: 你们有QPS最高的服务是哪一个呀?QPS是多少呀? 你们限制设置多少呀?

A:我们QPS最高的是A服务,里面有个A接口访问量特别大,QPS最高是1000,所以我们服务限流阈值设置的是1000

Q: 你们这个服务限流阈值是1000,那是不是意味着,当A接口极限状态下,打满了1000QPS, 服务上的其他接口RT就会变长或者超时呢?这块你们是怎么考虑的?

可能很多人开发中不会涉及到限流的逻辑(应为大部分的业务都没有啥高并发,我的也一样,哈哈),一般就是RPC, 或者Spring Cloud 框架自带的组件,进行一下简单的参数配置

A: 呃,刚才可能没说清楚,1000是A接口的限制,是我们自己在代码里面用的滑动窗口原理,实现的一个限流逻辑,只针对该接口限流,服务侧肯定也是要有限流的,但是一般是用框架层面的组件实现的限流。当然这里不可能设置1000,因为极限情况下单个接口的QPS就是1000,如果设置1000,确实服务上的其他接口没法返回。也没法保证服务整体的可用性,这个阈值,一般要参考监控系统监控到的历史峰值数据,一般为历史峰值数据的1.5或者2倍即可。

Q: 好,不错,我这有一个场景,我有一个A服务,部署3个容器节点,有一天我发现接口会出现时不时的超时或者失败的情况。你觉得可能是什么原因?

A: 嗯,好的我思考一下,首先,多节点部署,接口强调会出现时不时失败的情况,失败率有没有达到33.33%,如果是的话,肯定是有一个节点不可用了,大概率是崩溃了,而且能确定的是,上层的lb服务是均分的策略。如果失败率没有33.33%,则服务可能没崩溃,可能是负载太高了,请求处理不过来了。如果是的话,可以通过lb服务的限流或者是分流的能力来保证服务可用(这就是服务架构层面的负载均衡策略,我们后续在聊)。

3.怎么限流:

通过上面的对话我们应该就能得到结论了:

  • 接口限流:业务自己实现,计数,漏桶,令牌桶,滑动窗口等方法(滑动窗口选用最多的,建议大家自行对比一下,加深映像)
  • 服务限流:一般是利用框架层面的能力,但是阈值的设置需要参考历史峰值,不要随便一个数。
  • 架构限流:这里其实有点牵强,不能叫限流,应该叫流控,主要是依赖服务上游的LB服务能力,选择一个易于扩展的负载均衡策略,当服务节点出现问题的时候可以通过扩缩容,分发,导流等手段保证整体架构的可用性。

拓展那么多,我们还是得解决问题,文章开头的验证码防爆破问题,只需要在验证码登录的接口上用上滑动接口限流 + 黑名单的机制就可以了。

下面分享一端redis 实现滑动窗口的代码,其他实现方式,大家可自行探索,知道原理,一通百通:

     * 判断行为是否被允许
     *
     * @param userId        用户id
     * @param actionKey     行为key
     * @param period        限流周期
     * @param maxCount      最大请求次数(滑动窗口大小)
     * @return
     */
    public boolean isActionAllowed(String userId, String actionKey, int period, int maxCount) throws IOException {
        String key = this.key(userId, actionKey);
        long ts = System.currentTimeMillis();
        Pipeline pipe = jedis.pipelined();
        pipe.multi();

        // 每个用户一个 zset,这里是 user + key 组合
        pipe.zadd(key, ts, String.valueOf(ts));
        // 移除滑动窗口之外的数据
        pipe.zremrangeByScore(key, 0, ts - (period * 1000));
        // 获取窗口内的数量
        Response<Long> count = pipe.zcard(key);
        // 设置行为的过期时间,如果数据为冷数据,zset将会删除以此节省内存空间(不是必须,算是优化)
        pipe.expire(key, period);
        pipe.exec();
        pipe.close();
        return count.get() <= maxCount;
    }

知其然,更要知其所以然。

希望本文章对大家有用,欢迎留言交流,能力有限,不喜勿喷。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值