Web开发

注册

 

发新话题 回复该主题

2022年崔庆才Python3爬虫教 [复制链接]

1#
北京治疗白癜风去哪个医院好 https://wapyyk.39.net/hospital/89ac7_knowledges.html

随着大数据时代的发展,各个公司的数据保护意识越来越强,大家都在想尽办法保护自家产品的数据不轻易被爬虫爬走。由于网页是提供信息和服务的重要载体,所以对网页上的信息进行保护就成了至关重要的一个环节。

网页是运行在浏览器端的,当我们浏览一个网页时,其HTML代码、JavaScript代码都会被下载到浏览器中执行。借助浏览器的开发者工具,我们可以看到网页在加载过程中所有网络请求的详细信息,也能清楚地看到网站运行的HTML代码和JavaScript代码,这些代码中就包含了网站加载的全部逻辑,如加载哪些资源、请求接口是如何构造的、页面是如何渲染的等等。正因为代码是完全透明的,所以如果我们能够把其中的执行逻辑研究出来,就可以模拟各个网络请求进行数据爬取了。

然而,事情没有想象得那么简单。随着前端技术的发展,前端代码的打包技术、混淆技术、加密技术也层出不穷,借助于这些技术,各个公司可以在前端对JavaScript代码采取一定的保护,比如变量名混淆、执行逻辑混淆、反调试、核心逻辑加密等,这些保护手段使得我们没法很轻易地找出JavaScript代码中包含的的执行逻辑。

在前几章的案例中,我们也试着爬取了各种形式的网站。其中有的网站的数据接口是没有任何验证或加密参数的,我们可以轻松模拟并爬取其中的数据;但有的网站稍显复杂,网站的接口中增加了一些加密参数,同时对JavaScript代码采取了上文所述的一些防护措施,当时我们没有直接尝试去破解,而是用Selenium等类似工具来实现模拟浏览器执行的方式来进行“所见即所得“的爬取。其实对于后者,我们还有另外一种解决方案,那就是直接逆向JavaScript代码,找出其中的加密逻辑,从而直接实现该加密逻辑来进行爬取。如果加密逻辑实在过于复杂,我们也可以找出一些关键入口,从而实现对加密逻辑的单独模拟执行和数据爬取。这些方案难度可能很大,比如关键入口很难寻找,或者加密逻辑难以模拟,可是一旦成功找到突破口,我们便可以不用借助于Selenium等工具进行整页数据的渲染而实现数据爬取,这样爬取效率会大幅提升。

本章我们首先会对JavaScript防护技术进行介绍,然后介绍一些常用的JavaScript逆向技巧,包括浏览器工具的使用、Hook技术、AST技术、特殊混淆技术的处理、WebAssembly技术的处理。了解了这些技术,我们可以更从容地应对JavaScript防护技术。

1.引入

我们在爬取网站的时候,会遇到一些情况需要分析一些接口或URL信息,在这个过程中,我们会遇到各种各样类似加密的情形,比如说:

某个网站的URL带有一些看不太懂的长串加密参数,要抓取就必须要懂得这些参数是怎么构造的,否则我们连完整的URL都构造不出来,更不用说爬取了。分析某个网站的Ajax接口的时候,可以看到接口的一些参数也是加密的,或者RequestHeaders里面也可能带有一些加密参数,如果不知道这些参数的具体构造逻辑就没法直接用程序来模拟这些Ajax请求。翻看网站的JavaScript源代码,可以发现很多压缩了或者看不太懂的字符,比如JavaScript文件名被编码,JavaScript的文件内容都压缩成几行,JavaScript变量也被修改成单个字符或者一些十六进制的字符,导致我们不好轻易根据JavaScript找出某些接口的加密逻辑。

这些情况呢,基本上都是网站为了保护其本身的一些数据不被轻易抓取而采取的一些措施,我们可以把它归类为两大类:

URL/API参数加密JavaScript压缩、混淆和加密

这一节我们就来了解下这两类技术的基本原理和一些常见的示例。知己知彼,百战不殆,了解了这些技术的实现原理之后,我们才能更好地去逆向其中的逻辑,从而实现数据爬取。

2.网站数据防护方案

当今大数据时代,数据已经变得越来越重要,网页和App现在是主流的数据载体,如果其数据的API没有设置任何保护措施,在爬虫工程师解决了一些基本的反爬如封IP、验证码的问题之后,那么数据还是可以被轻松爬取到的。

那么,有没有可能在URL/API层面或JavaScript层面也加上一层防护呢?答案是可以。

URL/API参数加密

网站运营者首先想到防护措施可能是对某些数据接口的参数进行加密,比如说对某些URL的一些参数加上校验码或者把一些id信息进行编码,使其变得难以阅读或构造;或者对某些API请求加上一些token、sign等签名,这样这些请求发送到服务器时,服务器会通过客户端发来的一些请求信息以及双方约定好的秘钥等来对当前的请求进行校验,如果校验通过,才返回对应数据结果。

比如说客户端和服务端约定一种接口校验逻辑,客户端在每次请求服务端接口的时候都会附带一个sign参数,这个sign参数可能是由当前时间信息、请求的URL、请求的数据、设备的ID、双方约定好的秘钥经过一些加密算法构造而成的,客户端会实现这个加密算法构造sign,然后每次请求服务器的时候附带上这个参数。服务端会根据约定好的算法和请求的数据对sign进行校验,如果校验通过,才返回对应的数据,否则拒绝响应。

当然登录状态的校验也可以看作是此类方案,比如一个API的调用必须要传一个token,这个token必须用户登录之后才能获取,如果请求的时候不带该token,API就不会返回任何数据。

倘若没有这种措施,那么基本上URL或者API接口是完全公开可以访问的,这意味着任何人都可以直接调用来获取数据,几乎是零防护的状态,这样是非常危险的,而且数据也可以被轻易地被爬虫爬取。因此对URL/API参数一些加密和校验是非常有必要的。

JavaScript压缩、混淆和加密

接口加密技术看起来的确是一个不错的解决方案,但单纯依靠它并不能很好地解决问题。为什么呢?

对于网页来说,其逻辑是依赖于JavaScript来实现的,JavaScript有如下特点:

JavaScript代码运行于客户端,也就是它必须要在用户浏览器端加载并运行。JavaScript代码是公开透明的,也就是说浏览器可以直接获取到正在运行的JavaScript的源码。

由于这两个原因,至使JavaScript代码是不安全的,任何人都可以读、分析、复制、盗用,甚至篡改。

所以说,对于上述情形,客户端JavaScript对于某些加密的实现是很容易被找到或模拟的,了解了加密逻辑后,模拟参数的构造和请求也就是轻而易举了,所以如果JavaScript没有做任何层面的保护的话,接口加密技术基本上对数据起不到什么防护作用。

如果你不想让自己的数据被轻易获取,不想他人了解JavaScript逻辑的实现,或者想降低被不怀好意的人甚至是黑客攻击。那么就需要用到JavaScript压缩、混淆和加密技术了。

这里压缩、混淆和加密技术简述如下:

代码压缩:即去除JavaScript代码中的不必要的空格、换行等内容,使源码都压缩为几行内容,降低代码可读性,当然同时也能提高网站的加载速度。代码混淆:使用变量替换、字符串阵列化、控制流平坦化、多态变异、僵尸函数、调试保护等手段,使代码变地难以阅读和分析,达到最终保护的目的。但这不影响代码原有功能。是理想、实用的JavaScript保护方案。代码加密:可以通过某种手段将JavaScript代码进行加密,转成人无法阅读或者解析的代码,如借用WebAssembly技术,可以直接将JavaScript代码用C/C++实现,JavaScript调用其编译后形成的文件来执行相应的功能。

下面我们对上面的技术分别予以介绍。

3.URL/API参数加密

现在绝大多数网站的数据一般都是通过服务器提供的API来获取的,网站或App可以请求某个数据API获取到对应的数据,然后再把获取的数据展示出来。但有些数据是比较宝贵或私密的,这些数据肯定是需要一定层面上的保护。所以不同API的实现也就对应着不同的安全防护级别,我们这里来总结下。

为了提升接口的安全性,客户端会和服务端约定一种接口校验方式,一般来说会使用到各种加密和编码算法,如Base64、Hex编码,MD5、AES、DES、RSA等对称或非对称加密。

举个例子,比如说客户端和服务器双方约定一个sign用作接口的签名校验,其生成逻辑是客户端将URLPath进行MD5加密然后拼接上URL的某个参数再进行Base64编码,最后得到一个字符串sign,这个sign会通过RequestURL的某个参数或RequestHeaders发送给服务器。服务器接收到请求后,对URLPath同样进行MD5加密,然后拼接上URL的某个参数,也进行Base64编码也得到了一个sign,然后比对生成的sign和客户端发来的sign是否是一致的,如果是一致的,那就返回正确的结果,否则拒绝响应。这就是一个比较简单的接口参数加密的实现。如果有人想要调用这个接口的话,必须要定义好sign的生成逻辑,否则是无法正常调用接口的。

当然上面的这个实现思路比较简单,这里还可以增加一些时间戳信息增加时效性判断,或增加一些非对称加密进一步提高加密的复杂程度。但不管怎样,只要客户端和服务器约定好了加密和校验逻辑,任何形式加密算法都是可以的。

这里要实现接口参数加密就需要用到一些加密算法,客户端和服务器肯定也都有对应的SDK实现这些加密算法,如JavaScript的crypto-js,Python的hashlib、Crypto等等。

但还是如上文所说,如果是网页的话,客户端实现加密逻辑如果是用JavaScript来实现,其源代码对用户是完全可见的,如果没有对JavaScript做任何保护的话,是很容易弄清楚客户端加密的流程的。

因此,我们需要对JavaScript利用压缩、混淆等方式来对客户端的逻辑进行一定程度上的保护。

4.JavaScript压缩

这个非常简单,JavaScript压缩即去除JavaScript代码中的不必要的空格、换行等内容或者把一些可能公用的代码进行处理实现共享,最后输出的结果都压缩为几行内容,代码可读性变得很差,同时也能提高网站加载速度。

如果仅仅是去除空格换行这样的压缩方式,其实几乎是没有任何防护作用的,因为这种压缩方式仅仅是降低了代码的直接可读性。如果我们有一些格式化工具可以轻松将JavaScript代码变得易读,比如利用IDE、在线工具或Chrome浏览器都能还原格式化的代码。

比如这里举一个最简单的JavaScript压缩示例,原来的JavaScript代码是这样的:

functionecho(stringA,stringB){constname="Germey";alert("hello"+name);}

压缩之后就变成这样子:

functionecho(d,c){conste="Germey";alert("hello"+e);}

可以看到这里参数的名称都被简化了,代码中的空格也被去掉了,整个代码也被压缩成了一行,代码的整体可读性降低了。

目前主流的前端开发技术大多都会利用Webpack、Rollup等工具进行打包,Webpack、Rollup会对源代码进行编译和压缩,输出几个打包好的JavaScript文件,其中我们可以看到输出的JavaScript文件名带有一些不规则字符串,同时文件内容可能只有几行内容,变量名都是一些简单字母表示。这其中就包含JavaScript压缩技术,比如一些公共的库输出成bundle文件,一些调用逻辑压缩和转义成冗长的几行代码,这些都属于JavaScript压缩。另外其中也包含了一些很基础的JavaScript混淆技术,比如把变量名、方法名替换成一些简单字符,降低代码可读性。

但整体来说,JavaScript压缩技术只能在很小的程度上起到防护作用,要想真正提高防护效果还得依靠JavaScript混淆和加密技术。

5.JavaScript混淆

JavaScript混淆是完全是在JavaScript上面进行的处理,它的目的就是使得JavaScript变得难以阅读和分析,大大降低代码可读性,是一种很实用的JavaScript保护方案。

JavaScript混淆技术主要有以下几种:

变量混淆:将带有含义的变量名、方法名、常量名随机变为无意义的类乱码字符串,降低代码可读性,如转成单个字符或十六进制字符串。字符串混淆:将字符串阵列化集中放置、并可进行MD5或Base64加密存储,使代码中不出现明文字符串,这样可以避免使用全局搜索字符串的方式定位到入口点。属性加密:针对JavaScript对象的属性进行加密转化,隐藏代码之间的调用关系。控制流平坦化:打乱函数原有代码执行流程及函数调用关系,使代码逻变得混乱无序。无用代码注入:随机在代码中插入不会被执行到的无用代码,进一步使代码看起来更加混乱。调试保护:基于调试器特性,对当前运行环境进行检验,加入一些强制调试debugger语句,使其在调试模式下难以顺利执行JavaScript代码。多态变异:使JavaScript代码每次被调用时,将代码自身即立刻自动发生变异,变化为与之前完全不同的代码,即功能完全不变,只是代码形式变异,以此杜绝代码被动态分析调试。锁定域名:使JavaScript代码只能在指定域名下执行。反格式化:如果对JavaScript代码进行格式化,则无法执行,导致浏览器假死。特殊编码:将JavaScript完全编码为人不可读的代码,如表情符号、特殊表示内容等等。

总之,以上方案都是JavaScript混淆的实现方式,可以在不同程度上保护JavaScript代码。

在前端开发中,现在JavaScript混淆主流的实现是javascript-obfuscator(

分享 转发
TOP
发新话题 回复该主题