一、背景
短信验证码功能我们都不陌生,我们印象中的短信验证码都是有发送限制的(如60s内只能发送1次),这样做的目的一方面是为了保护系统,另一方面也是为了控制成本(发短信要钱的)。
我们先简单分析一下短信验证码接口被狂刷的主要场景有哪些:
- 同一用户id狂刷
- 同一ip狂刷(也就是用不同用户id狂刷)
- 所有用户id都狂刷
二、解决方案
要解决上面提到的问题,我们大概思路可以分为限流、缓存和安全
限流方案可以分为以下几种:
- 客户端限制用户频繁发送:如按钮置灰防止用户频繁点击
- 服务端限制用户频繁发送:客户端限制用户频繁发送的方案可能会被绕过,所以我们需要双重保证,通过记录上次发送时间点来判断是否在60s内发送过(可以配置多重策略,如60s只能发送1次、24h只能发送10次等)
- ip限流:可以通过滑动窗口算法来记录同一个ip在短时间内发送的短信验证码条数,来达到限制不同用户id狂刷的问题(即上面两种方案)
- 验证码缓存:一些安全性不高的场景(如绑定手机等,其他敏感场景如银行转账等则不适用)可以采用验证码缓存的方式,在短时间内重复请求会返回已缓存的验证码(即多次发也只能生成1条短信)
- token校验:将短信验证码接口加上token校验,每次请求都需要携带服务端返回的唯一token标识才能正常响应
- 动态黑名单:可以通过一些算法将疑似恶意刷接口的用户/ip给添加到黑名单,限制他们的接口请求(也可能出现误封,需要提供手动解封的功能)
三、Q&A
1、每天只能发送10次的限制要按自然日还是过去24小时?
正常来说是按过去24小时,因为如果是自然日的话,可能会出现23:59:59发送了10条短信,而00:00:00又发送了10条短信,可能会出现性能瓶颈等问题。
2、如何实现滑动窗口算法?
// 大概思路是使用Redis的Zset数据结构,因为考虑到它的速度快和有序(可用来存时间戳)