likes
comments
collection
share

从发送短信验证码来研究几种常用的防刷策略

作者站长头像
站长
· 阅读数 2

大家好,我是小趴菜,最近在做项目的时候有个发送短信验证码的需求,这个需求在大部分的项目中相信都会使用到,而发送短信验证码是需要收费的,所以我们要保证我们的接口不能被恶意刷,

1:前端控制

前端控制是指在用户点击发送验证码之后,在一分钟之内这个按钮就置灰,让用户无法再次发起,这种方式有什么优点和缺点呢?

优点

  • 1: 实现简单,直接让前端进行控制

缺点

  • 1:安全性不够,别人完全可以绕过前端的控制,直接发起调用,这种方式只能作为防刷的第一道屏障

2:redis + 过期时间

在用户发送验证码之后,将用户的手机号作为redis的KEY,value可以设置为任意值,并且将该KEY的过期时间设置为1分钟,实现流程如下:

  • 1:用户客户端发起发送验证码
  • 2:后端收到请求以后,将该用户的手机号作为KEY,VALUE设置为任意值,并且是过期时间为1分钟
  • 3:当用户下次发起发送验证码请求,后端可以根据用户手机号作为KEY,从Redis中获取,如果这个KEY不存在,说明已经过去1分钟了,可以再次发送验证码
  • 4:如果这个KEY存在,说明这个用户在一分钟内这个用户已经发送过了,就提示用户一分钟后再试

那么这种方式又有什么优点和缺点呢???

优点

  • 1:实现简单
  • 2:由后端控制,安全性比前端控制高

缺点

  • 1:首先需要依赖Redis
  • 2:一分钟后这个KEY真的能被准时删除吗????

针对第2点我们深入分析下,正常来说,一个Redis的KEY,设置了1分钟过期时间,那么在1分钟后这个KEY就会被删除,所以这种redis+过期时间在正常情况下是可以满足防刷的,但是Reids真的能帮我们准时的删除这个KEY吗?

在此我们不得不了解下Redis的删除策略了,redis有三种删除策略

  • 1:定时删除:会给这个KEY设置一个定时器,在这个KEY的过期时间到了,就会由定时器来删除这个KEY,优点是可以快速释放掉内存,缺点就是会占用CPU,如果在某个点有大量的KEY到了过期时间,那么此时系统CPU就会被沾满
  • 2:惰性删除:当这个KEY过期了,但是不会自动释放掉内存,而是当下次有客户端来访问这个KEY的时候才会被删除,这样就会存在一些无用的KEY占用着内存
  • 3:定期删除:redis会每隔一段时间,随机抽取一批的KEY,然后把其中过期的KEY删除

如果reids设置的删除策略是定期删除,那么你这个KEY即使到了过期时间也不会被删除,所以你还是可以在Redis中获取到,这个时候客户端明明已经过了一分钟了,但是你还是能拿到这个KEY,所以这时候又会被限制发送验证码了,这明显不符合业务需求了

所以一般会采用惰性删除+定期删除的方式来实现,这样,即使定期删除没有删除掉这个KEY,但是在访问的时候,会通过惰性删除来删除掉这个KEY,所以这时候客户端就访问不到这个KEY,就可以实现一分钟内再次发送验证码的请求了

但是如果你的Redis是做了读写分离的,也就是写操作是写主,查询是从,那么这时候会有什么问题呢?

我们在设置Redis的过期时间有四种命令

  • 1:expire:从当前时间算起,过了设置的时间以后就过期
  • 2:pexpire:同expire,只是过期时间的单位不一样
  • 3:expireAt:设置未来的某个时间,当系统时间到了这个点之后就过期
  • 4:pexpireAt:同expireAt,只是过期时间单位不一样

如果我们使用的是expire命令来设置时间,redis主从同步是异步的,那么在这期间一定会有时间差,当主同步到从的时候,可能已经过去十几秒都有可能,那么这时候从redis收到这个KEY以后,是从当前时间开始算起,然后过去指定的时间以后才会过期,所以这时候主redis这个KEY过期了,但是从redis这个KEY可能还有十几秒以后才会过期

这时候你查的是从Redis,所以还是可以查到这个KEY的,这时候客户端其实已经过去一分钟了,但是由于你能从Redis查到这个KEY,所以客户端还是不能发送验证码

这时候我们可以使用expireAt命令来设置,只要系统到了这个时间点,这个KEY就会被删除,但是前提是要保证主从Redis系统的时间一致,如果你从库的时间比主库晚了几分钟,那么从库这个KEY存活的时间就会比主Redis存活的时间更长,那么这样也会有问题

redis + 特殊VALUE + 过期时间

这种的业务流程如下

  • 1:用户客户端发起发送验证码
  • 2:后端收到请求以后,将该用户的手机号作为KEY,VALUE设置为当前时间戳(重点)
  • 3:当用户下次发起发送验证码请求,后端可以根据用户手机号作为KEY,从Redis中获取,如果这个KEY不存在,可以再次发送验证码
  • 4:如果这个KEY存在,获取到这个KEY的VALUE,然后判断当前时间戳跟这个KEY的时间戳是否超过1分钟了,如果超过了就可以再次发送,如果没有就不能发送了

这种方式与其它几种方式的优点在哪呢?

无论你这个KEY有没有准时被删除,删除了说明可以发送,即使因为某些原因没有被删除,那么我们也可以通过设置的VALUE的值跟当前时间戳做一个比较。所以即使出现了上面 redis + 过期时间会出现的问题,那么我们也可以做好相应的判断,如果你过去一分钟还能拿到这个KEY,并且比较时间戳也已经超过一分钟了,那么我们可以重新给这个KEY设置VALUE,并且值为当前时间戳,就不会出现以上的几种问题了。

结尾

题外话,其实KEY即使时间到期了,但是我们还是能查到这个KEY,除了之前说的几个点,还有几种情况也会出现,Redis删除KEY是需要占用CPU的,如果此时你系统的CPU已经被其它进程占满了,那么这时候Redis就无法删除这个KEY了