内容简介:背景本文主要探讨接口在使用动态签名的机制下,爬虫与接口的相互攻防策略。故事开始
导语
本文通过爬虫与接口服务的攻防回合制,简单介绍如何使用签名来提高接口安全性。道高一尺,魔高一丈。道不服,再高1.5丈。
背景
如何提高api 接口的安全性?
服务端将接口提供给客户端,在公网开放访问,非常危险。我们既要确保客户端与服务端之间的安全通信,又要考虑防爬防篡改等恶意攻击。没有绝对安全的接口,我们只能做的让接口相对安全一些。提高接口安全性的方法一般分为以下几个方面:
-
服务端鉴别:服务端根据请求特征、行为等判断请求是否是爬虫或者机器。
-
动态签名:针对每次的请求,根据参数不同,按照一定算法规则动态生成相应的签名,服务端验证签名合法性,主要防篡改。
-
身份验证:需要用户登录,后端在处理接口之前先校验用户信息合法性,然后再处理接口。
-
数据加密:针对安全性要求较高的请求做数据加密,比如游戏密令登录,支付转账等。
本文主要探讨接口在使用动态签名的机制下,爬虫与接口的相互攻防策略。
故事开始
一个很普通的晚上,一段代码悄悄运行,代号 -爬虫。
接口的访问量突然升高报警不断。就这样一段爬虫与接口的博弈,拉开序幕。
牛刀小试
1、 【攻】数据采集 -- 脚本抓取
爬虫找到数据接口,通过for 循环更改page 页码参数,很轻松的采集到了该接口下的所有数据。但并不满足,还写了定时脚本,每天晚上了2点准时开跑。
突然有一天,接口失效了。接口由原来的https://xxx.58.com/zufang/list?page=1变为https://xxx.58.com/zufang/list?page=1&sign=de2e67afcc554c1840886a96e2211589 。
是的,爬虫的行为被接口察觉到了,接口反手做了签名处理。
2、 【防】参数篡改 -- 尾随签名
首先,业务代码中需要安装一个请求接口的sdk,sdk中封装了签名逻辑。
SDK中签名步骤大致如下:
-
将所有业务请求body参数序列化成一个json。
-
body参数和url中的请求参数通过Sign函数md5加密得到签名sign。
-
将签名追加在url 参数中,随请求一起发送。
经过签名处理后,爬虫将参数篡改后发送的请求,接口能够识别出来,并直接拒掉返回。
崭露头角
然而并没有难倒爬虫,url 虽然经过了签名处理,但参数相同的情况下,签名出来的url是固定的。
1、【攻】参数签名-- url 收集
大致步骤如下:
-
用一个数组将每个接口完整地址(含签名)收集并存储起来。
-
循环将数组中的接口挨个请求。
收集url过程虽然有点麻烦,但却是一劳永逸的事。
接口侧很快发现之前的策略并没有有效的制止住爬虫,于是做了升级
2、【防】url重复利用 -- 签名引入时间
客户端计算签名时候,请求参数中添加t=当前时间,然后计算签名,当前时间随参数发送到后端,后端校验签名的时间是否在一段时间范围内,如果超过一定时间,则判断签名非法。
大致步骤如下:
-
计算签名时,将当前时间一起算入签名中。
-
发送请求时候,将时间与签名放入参数中,一起随请求发送。
-
后端可根据签名,验证参数的合法性,根据时间,判断签名有没有过期。
引入时间因子后,签名出来的url在一定时间后会失效。爬虫无法将一个签名长期使用。
针锋相对
爬虫发现请求参数、时间均可在客户端生成,既然不能重复的利用现有的签名,那就自己去生成签名。
1、【攻】动态签名 -- 签名伪造
自己生成签名,其实不易,但阻止不了一个爱钻牛角尖的爬虫。
A、逆向代码,肉眼找到相关逻辑
B、借助 工具 格式化
C、抽取签名函数,爬虫主程序修改参数重新计算签名,然后抓取。
提取出签名函数后,重新计算签名,与正常用户请求的数据一模一样。并且签名所需要的各种因素均可自给自足,简单方便有效。
接口不得不再次升级,之前只对签名正确性做校验,没有对签名合法性做校验。所以决定在签名因子中加入一个token字符串。这个字符串,只能后端生成,后端校验。
2、【防】客户端伪造签名 -- 引入token
-
服务端收到请求,首先验证cookie 中的token 是否合法,然后验证签名是否合法。
-
服务端在返回数据时,将重新生成token 随cookie下发至浏览器。
SDK中根据token生成签名代码如图:
整个流程大致如下:
-
生成签名时,客户端从cookie 中获取 token 值。将token 值加入到签名规则中。
-
服务端收到请求,首先验证cookie 中的token 是否合法,然后验证签名是否合法。
-
服务端在返回结果时,在cookie 中重新设置新的token。
-
客户端第一次请求,当cookie中没有token时候,服务端返回特殊的错误状态码,sdk 识别这个状态码,会进行第二次请求(第二次会拿到第一次返回的token)。
token 由后端生成,随cookie 下发,客户端无法伪造,也不知道生成规则,无法自给自足伪造签名。
神仙打架
一段时间后,爬虫发现接口失效,排查发现接口并没有增加新的参数,而且发现一个奇怪的现象:
在控制台中抓到一个链接,能正常返回数据。然后将这个链接拷贝出来,在浏览器访问,则提示请求非法。
爬虫百思不得其解。同样的浏览器,同样的环境,上一次请求,第二次就不行了。两次请求区别在哪儿?
此时一道的闪电劈中爬虫的天灵盖,脑中闪过一道灵感 “cookie 中有诈”
经过爬虫分析,发现每次签名都会将cookie 中的一个字符串带入进去,并且,每次请求完成后,这个cookie 被响应头给重置了。所以一个链接在浏览器里面第二次使用的时候,cookie 里面的token 已经发生变化,导致后端校验不通过。
爬虫很快对程序进行修改。
1、【攻】动态签名 -- 浏览器行为模拟
大致步骤如下:
-
第一次请求,必返回异常(没有token),将返回header 中的cookie 记录下来用于第二次请求。
-
请求中随机构造浏览器ua 等参数,突破后端的反爬策略。
-
从第二次请求开始,每次请求返回的token,用map 存储起来,便于下次使用。
v4.0的爬虫犹若开启了神智,签名不管什么策略,复杂程度,都能模拟。而且市面上大部分的签名程序都能通过相似的步骤破解 。
接口方脑袋疼。爬虫请求来的数据和正常用户的一模一样,还有没有什么办法辨别?
同样的,那道劈中爬虫的闪电又劈中了接口的天灵盖。
2、【防】爬虫利用token -- 行为统计-
token 的设计,一千个人就有一千个设计。token 的生成和校验是一个高频行为,所以token的生成要保证高效 、加密,随机、体积小。
-
爬虫与正常用户最大的差别是行为上访问频次不同,如果token 能记录下一个用户在一段时间内的访问频次,那从行为上可以区分出爬虫和正常用户,所以我们token 的设计除了用来校验合法性以外,我们还要记录用户一段时间内的访问频次。
-
接口采用9位数组存放token, token中包含随机数,时间,请求量,以及加密校后的校验位置。
-
服务端不对token 做存储,只校验token 的合法性校验。
-
t在一定允许时间内,token中的时间不更新,用做漏斗计数,记录用户在这段时间内的访问频次。
token 中采用漏斗计数策略后,从访问频次大致可以区分爬虫和正常访问。从而拒绝掉一些高频流量访问。
3、还有一个问题,如果接口每次都不利用上一次token,token传空怎么处理?
这儿在sdk 侧有个简单处理,当token 为空时,后端返回一个token 为空的错误状态,但返回错误状态时,cookie中会下发token。sdk此时会进行第二次请求,第二次则能够拿到正确数据。
爬虫也可以利用这个原理,绕开token行为统计。
接口其实还有一层处理,
异常token IP计数 。
上面代码中:
没有token的请求,我们拿到ip ,将ip转换为32位int,在一个int 的map中加一,并判断map中这个ip是否超过一定阈值,如果超过则拒绝返回。
4、代码中有几个细节简单说明一下:
A、为什么这个规则只应用于没有token的请求,而不应用于所有的请求。
应用于所有请求,容易大面积误伤。学校,机场等公共wifi,大部分人的出口ip是同一个,很容易命中策略。而对没有token的请求应用此策略,既能做到ip限制也可以降低误伤可能性
B、为什么不直接用ip 字符串作为key,需要转换为int作为key?
32位int 占4字节,在node 中是一个number 最多也就8字节,而字符串的话所占字节位7-15字节,int 存储可节省内存。
map 中number 作为key 效率相当于数组下标寻址,而用字符串做key,map 中还需要转一遍hash。number key 在存储和查询效率上会高出一大截。
5、说明完毕,回到正文
爬虫发现接口抓取在一定数量后,接口返回就异常了。一定是通过啥手段识别出来短时间内访问太多,爬虫进行了更进一步的调整。点到为止
爬虫对着忽有忽无的接口,大致也猜到了接口的一些限流处理。然而并没有难住他。
1、【攻】高频被拒 -- 限流访问
显示的规则破解sdk可以拿到签名代码,隐示规则可以自己写爬取策略,换ip、限频次、模拟行为、伪造内容,防不胜防。
策略全开的爬虫,有了那么一点灵性。这点灵性,接口如何来应对?
接口祭上了一份小小礼物。
2、【防】反编译 -- 混淆加密-
sdk 代码不宜全部混淆,混淆核心代码就行了。
原始代码:
将核心代码加密
加密的目是为了提高破解的难度,并不是从理论层面去达到代码的不可逆。所以我们加密的选型大致从两个维度考虑。
A、采用市面上已有的不可逆加密(至少不容易)
已有的将代码混淆的加密方案 Eval,
Array,_Number,JSfuck,JJencode,AAencode,URLencode,Packer,JS Obfuscator,My Obfuscate。均可被解密,且都能找到相应的开源解密工具,(不过可以使用两种以上的混淆方式,达到混合混淆,破解也有一定难度)。
上图代码采用的国内的jshaman 加密,不能保证完全不可逆,但至少还没有一个开源的将代码一粘就可以逆回来工具。
B、自己实现简单的可逆加密,但是逆向工程需要自己实现。
例如自己修改jjencode加密算法,然后混淆代码如下图:
首先说明一下,以上代码是可逆的,混淆逻辑就是将jjencode混淆函数中的混淆变量做了一些替换,逻辑上做了一些调整。但替换后有两个好处:
-
无法用肉眼识别是采用的哪种混淆方案。
-
无法用市面上现有的开源解密工具解密出来,除非爬虫精通市面上的大部分混淆方案,然后自己实现一个解密函数。
代码加密有什么用呢?加密代码和不加密代码,放在浏览器控制台不都能运行出相同的结果?
是的,在浏览器端都能运行出相同的结果,但是加密后的代码在node端则无法正常运行出正确的结果(黑人问号.jpg)。
例如代码中判断 window.document 对象是否存在,如果存在,则走正常的签名,如果不存在则返回错误的签名。由于混淆后的代码,不易反编译,所以爬虫无法知道里面的判断逻辑,无法伪造浏览器环境。进一步提高破解门槛。
秋色平分
1、【攻】混淆加密 -- 完全浏览器模拟
逆向加密后的代码比较困难,但有个思路,本地装一个浏览器内核,调用浏览器api 执行js 就可以抓取数据了。
结尾
爬虫与接口没有绝对的胜负,两者的攻防较量都了献上了自己的智慧,当爬虫具备一些灵智,开始模拟用户环境和行为后,接口辨别爬虫与正常用户变得更为困难。接口签名只是提高提高伪造门槛,属于防爬的一个环节,拦住一些低级的爬虫。
当爬虫真的能完全模拟浏览器后,接口可开启接入反爬服务。反爬服务中,对ip,浏览器ua等有全方面的分析,爬虫虽然能完全模拟浏览器行为,但爬虫并没有那么多机器用来来部署,ip是有限的,最终也会被反爬
服务识别出来。但反爬服务会对接口性能造成一定影响,所以可作为最后的防线,选择性的开启。
本文 完
作者简介
龚虹宇,2017年8月加入房产事业部。主要负责58商业地产业务与前端基础服务的建设工作。
阅读推荐
以上所述就是小编给大家介绍的《前端爬虫攻防之接口签名方案》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
React 进阶之路
徐超 / 清华大学出版社 / 2018-4 / 69.00元
《React进阶之路》详细介绍了React技术栈涉及的主要技术。本书分为基础篇、进阶篇和实战篇三部分。基础篇主要介绍React的基本用法,包括React 16的新特性;进阶篇深入讲解组件state、虚拟DOM、高阶组件等React中的重要概念,同时对初学者容易困惑的知识点做了介绍;实战篇介绍React Router、Redux和MobX 3个React技术栈的重要成员,并通过实战项目讲解这些技术如......一起来看看 《React 进阶之路》 这本书的介绍吧!