ToB企服应用市场:ToB评测及商务社交产业平台

标题: 一种优雅的方式整合限流、幂等、防盗刷 [打印本页]

作者: 九天猎人    时间: 2024-9-1 13:30
标题: 一种优雅的方式整合限流、幂等、防盗刷
大家在工作中肯定遇到过接口被人狂刷的履历,就算没有履历过,在接口开发的过程中,我们也需要对那些轻易被刷的接口大概和会消耗公司款项相关的接口增加防盗刷功能。例如,发送短信接口以及发送邮件等接口,我看了国内许多产品的短信登录接口,基本上都是做了防盗刷,如果不做的话,一夜之间,也许公司都赔完了┭┮﹏┭┮。
假设我们正在开发一个发送短信(仅国内)的接口,过程如下
上面便是一个最简朴的向手机号发送短信验证码的接口,不思量其他和业务相关的操作。我们现在来分析一下,该接口存在的问题(刷接口)。
既然我们知道了发送短信验证码接口存在的缺陷,那我们将这些问题一一解决了,是不是就可以避免接口被盗刷呢?答案是只能在一定程度上防止被盗刷,因为这些恶意请求中,手机号都是通过程序无限生成的,都能通过我们的正则校验,所以对手机号进行发送次数和发送间隔限制,对他们是没有任何效果的。另外,想要避免向空号手机号发送短信的话,还需要额外的引入第三方的空号检验Api,增加了新的资源消耗。
我们现在从发送短信验证码的接口转移到其他的接口来看看,寻找一种可以大概应用于全部的接口,并能实现限流,幂等,防盗刷功能的方案。
公众号: 后端随笔
个人博客:https://knowledge.xcye.xyz/
解决接口请求参数轻易被构造

我们其实不难发现,导致接口被盗刷的根本原因在于请求参数很轻易通过算法构造构造出来,这些通过程序生成的参数,在我们的程序看来,都是正当的。
  1. {
  2. "phone": "11位手机号"
  3. }
复制代码

通过上面两个对比,我们不难发现,先对于只有一个参数phone的发送短信接口来说,想要构造出淘宝发送短信的参数,难度直接上升了许多个阶梯。
我们从解决接口请求参数轻易被构造的角度出发,我目前能想到的只有对请求参数进行加密,使用非对称加密的方式。具体的思路为,客户端在发送请求之前,使用服务端提供的公钥对请求参数进行加密,让请求参数看上去不那么轻易被构造出来。服务端获取到请求参数后,使用私钥进行解密,然后再进行后续的一些验证操作。
那么这样可以防止接口被盗刷么?答案是,只能防君子,不能防小人。特殊是对于Web端来说,如果发起盗刷的这个人,同样是一个开发者,他直接F12就可以从js文件中找到公钥。对于App来说,获取源码的方式会更难一点,但是最终公钥应该还是可以大概被找到的。
如果我们解决公钥轻易被获取的问题,是不是可以通过这种方式防止接口被盗刷呢?如果可以大概解决公钥轻易被获取的问题,在一定程度上,确实是可以解决接口被盗刷的问题,但是现在又将问题转移到了获取公钥接口上,我们还是需要解决获取公钥接口被盗刷的问题。
而且如果获取到的公钥不能存在时效性,可以被多次使用,那么这些通过加密实现防盗刷的接口,在公钥被泄漏的环境下,还是会存在被盗刷的问题。想要解决的话,可以让公钥只能使用一次,大概只能在很短时间内使用,再者只能被多少个请求使用。我最终的解决方案也是类似于这个,让令牌只能使用一次。
而且使用公钥进行加密,通常是防止在请求过程中发生的中心人攻击,是为了解决参数被修改以及泄漏的问题。
Ticket机制

我最终并不是通过解决参数轻易被构造来防止盗刷的,我是通过对请求进行是否是呆板人判断,如果是非法请求,强制必须先通过图形验证码,只有正当的请求,服务端才会进行处理。
我基于Ticket机制,客户端在发送请求之前,必须先向服务端申请一个Ticket。服务端在处理申请Ticket请求时,对请求进行判断,判断包含了是否是恶意请求和是否需要进行限流。当这两步都通过后,服务端会生成一个被加密,存在时效性并且只能使用一次的Ticket,客户端发送真正请求时,需要携带这个Ticket。每个Ticket只能被使用一次,而且客户端每次都携带Ticket,还可以通过Ticket实现请求的幂等性。
这种方案并不和任何的接口耦合,Ticket是携带在请求头上,不会对请求参数造成污染。
申请Ticket

我最终是使用Ticket完成了限流,防盗刷,幂等性这三个功能,为了让这个功能更加的通用,不和任何的接口相耦合。在申请Ticket时,客户端需要传递两个参数,分别是serviceType和primaryKey。serviceType用于控制该接口的类型,而primaryKey会被用于限流。最终结合配置中心,做到了可以大概轻松的对任何类型的请求进行独立的限流,UserAgent黑名单与白名单,Ip限流等操作。
具体的执行过程为(以发送短信验证码为例):

服务端返回的Ticket是加密后的密文,存在过期时间,保存在Redis中,并且只能被使用一次,无法被客户端构造出来。只管加密算法被不小心泄漏,服务端也无法从Redis中查询到这个"正当的Ticket",所以这个Ticket是足够安全的。
图形验证码

调用申请Ticket接口后,响应参数中包含两个参数:captchaStatus, ticket。captchaStatus体现该Ticket是否需要客户端通过图形验证码。
当captchaStatus为true时,客户端调用另一个接口加载图形验证码,在调用接口时,需要携带上一步获得的Ticket,服务端最终会将本次的图形验证码和Ticket进行绑定,最终实现在下一步中通过Ticket获取图形验证码的验证结果,具体步骤为:

在防盗刷功能中,最有效的还得是验证码功能
服务端验证Ticket

当客户端完成上面两个后,客户端现在才开始调用真正的接口(发送短信)。在调用发送短信验证码时,客户端需要携带申请到的Ticket和图形验证码Key(如果captchaStatus为true)。
服务端接收到请求后,具体的处理步骤如下:

上面便是我实现接口防盗刷的具体过程,现在我们来验证一下,这个防盗刷是否真的能防(还是以发送短信验证码)?
在上面的过程中,服务端验证请求是否是呆板人,还可以在发送真正请求时进行验证,如果验证失败,客户端根据响应体执行对应的操作,然后携带Ticket重发请求。
上面的逻辑并没有对正常用户的验证结果进行缓存,这会导致,正常用户在调用这些接口时,每调用一次,都需要通过图形验证码。
其他措施

另有其他的措施,也可以增加接口被盗刷的环境。这些措施包括增加防盗刷逻辑被破解难度和防止接口被盗刷。
先说防止接口被盗刷,本质上是防止接口被泄漏。对于App来说,某个人想要知道我们接口信息的话,必须对App进行反编译,我对App反编译不太了解,可以试试那些增大反编译的措施,就算不进行反编译,使用Fiddler工具也是可以看到请求信息的。对于Web端来说,用户只需要按F12就可以看到JavaScript代码,以及每个请求的参数,响应体等。我们可以禁用F12以及右键(降低用户体验),以及在生产环境中,添加当用户按F12后,自动进入无限Debug模式。这两个操作可以增加我们接口被袒露的风险,从而在一定程度上起到"防盗刷"目的。
对于增加防盗刷逻辑被破解难度来说,市场上有许多的App的限流等规则都被人攻破了,我个人觉得会被攻破,除了接口设计的原因外,另有一个是接口的响应体中提示了很明显的错误信息。好比我们访问某个增加了防刷功能的接口,该接口提示UserAgent无效,当前Ip已被限流,Ticket无效,未进行图形验证码验证等很明显的信息。这些信息其实已经间接提示了让请求变正当的步骤是什么,这虽然可以资助开发人员进行调试,但也间接的资助了那些发送恶意请求的人。所以为了增大防盗刷逻辑被破解的难度,我们不需要返回这些很明显的提示信息,可以无论什么原因,都返回"非法请求",对于公司开发人员来说,他们自己通过code从开发文档中查询每个code所代表的意思。
以上便是我对于防止接口被盗刷的一些见解,可能另有更优的方案,但是我目前确实只能想到这一种。另外,也可以使用已有的服务,好比腾讯云和阿里云等服务商的验证码。我使用的图形验证码是开源的,来自于dromara大佬开源的Java 行文验证码,使用起来非常的方便,并且支持滑块,旋转,滑动,文字点选,非常感谢大佬。此外,因为每次请求时申请到的Ticket都是加密的,在加密息争密的过程中,性能消耗也是一个可以优化的点,具体得看自己选择的算法是什么。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。




欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/) Powered by Discuz! X3.4