Python防爬进阶:JS逆向+签名算法破解深度实战
前阵子爬某大厂的公开数据接口发现请求参数里多了个sign字段每次请求都会变直接复制之前的请求过去直接返回403折腾了三天终于把整个签名算法破解了现在爬他们的接口再也不会被封了。今天把完整的JS逆向和签名破解流程分享给大家从抓包到最终Python实现全步骤详解跟着做你也能破解大部分网站的签名加密。先搞懂原理签名验证是怎么回事现在稍微有点规模的网站接口都会加签名验证前端在发送请求之前会把请求参数、时间戳、随机字符串等用约定好的算法比如MD5、SHA1、AES、RSA等加密生成一个sign参数后端收到请求后会用同样的算法重新计算一遍签名和前端传过来的sign比对如果不一致就直接拒绝请求加密逻辑都在前端JS代码里只要我们能找到这个加密函数就能自己生成合法的sign前期准备必备工具做JS逆向不需要你精通前端只要会用浏览器调试工具就行准备这些工具Chrome浏览器F12开发者工具必备插件Tampermonkey油猴脚本方便注入调试代码、XHR断点拦截请求Python库requests发请求、execjs运行JS代码、pycryptodome加密算法实现# 安装Python依赖pipinstallrequests execjs pycryptodome-ihttps://mirrors.aliyun.com/pypi/simple/实战步骤1抓包分析找到签名参数首先我们要确定哪个是签名参数以及签名是怎么生成的打开Chrome F12切换到Network面板刷新页面找到你要爬的接口请求查看请求参数找到每次请求都会变的那个参数一般叫sign、signature、_sig、token之类的多请求几次看看哪些参数会变一般时间戳timestamp、随机数nonce、签名sign这三个会每次变举个例子某接口的请求参数GET /api/data? page1 page_size10 timestamp1711536000 nonceabc123def signa1b2c3d4e5f6g7h8i9j0这里的sign就是我们要破解的签名参数。实战步骤2断点调试找到加密入口找到加密函数的位置是JS逆向最关键的一步有几个常用方法方法1全局搜索关键词在Chrome开发者工具的Sources面板里按CtrlShiftF打开全局搜索搜索sign:、signature、encrypt、md5、sha1这些关键词就能快速定位到加密代码的位置。方法2XHR断点在Sources面板的XHR/fetch Breakpoints里添加断点填入你接口的URL关键词当请求发送的时候就会断住然后看调用栈就能找到加密的位置。方法3Hook关键函数如果加密用的是标准的MD5、SHA1等算法可以用Tampermonkey注入脚本Hook这些函数打印调用栈// Tampermonkey脚本Hook MD5函数(function(){varoriginal_md5window.md5;window.md5function(){console.log(MD5被调用了参数,arguments);console.trace();// 打印调用栈returnoriginal_md5.apply(this,arguments);}})();找到加密函数之后你会看到类似这样的代码functiongenerateSign(params){// 把参数按键排序varkeysObject.keys(params).sort();varstr;for(vari0;ikeys.length;i){strkeys[i]params[keys[i]];}// 拼接密钥strsecretabcdefg1234567;// MD5加密returnmd5(str).toLowerCase();}这个就是签名生成的核心逻辑实战步骤3抠出JS代码本地运行验证找到加密函数之后我们把相关的JS代码抠出来放到本地测试能不能生成和浏览器一样的签名把加密函数和它依赖的所有函数比如上面的md5函数都复制出来写一个测试用例用浏览器里的参数测试看生成的sign是不是和浏览器里的一致举个例子我们把刚才的代码保存为sign.js// 这里放抠出来的MD5函数代码functionmd5(str){// 省略MD5实现代码...}functiongenerateSign(params){varkeysObject.keys(params).sort();varstr;for(vari0;ikeys.length;i){strkeys[i]params[keys[i]];}strsecretabcdefg1234567;returnmd5(str).toLowerCase();}// 测试用例和浏览器里的参数一致vartestParams{page:1,page_size:10,timestamp:1711536000,nonce:abc123def};console.log(generateSign(testParams));// 输出应该和浏览器里的sign一致在Node.js里运行这个JS文件看看输出是不是和浏览器里的sign一样如果一样就说明我们抠对了实战步骤4Python实现签名算法不用跑JS如果不想每次都调用JS我们可以把加密逻辑用Python重写一遍速度更快importhashlibimporttimeimportrandomimportstringdefgenerate_sign(params,secretabcdefg1234567):# 参数按键排序sorted_keyssorted(params.keys())# 拼接参数sign_strforkeyinsorted_keys:sign_strf{key}{params[key]}# 拼接密钥sign_strfsecret{secret}# MD5加密转小写md5hashlib.md5()md5.update(sign_str.encode(utf-8))returnmd5.hexdigest().lower()# 测试params{page:1,page_size:10,timestamp:int(time.time()),nonce:.join(random.choice(string.ascii_lowercasestring.digits)for_inrange(10))}params[sign]generate_sign(params)print(params)# 生成的参数和浏览器里的一致如果是更复杂的加密算法比如AES、RSA可以用pycryptodome库实现# AES加密示例fromCrypto.CipherimportAESfromCrypto.Util.Paddingimportpadimportbase64defaes_encrypt(data,key,iv):cipherAES.new(key.encode(utf-8),AES.MODE_CBC,iv.encode(utf-8))encryptedcipher.encrypt(pad(data.encode(utf-8),AES.block_size))returnbase64.b64encode(encrypted).decode(utf-8)常见签名算法类型与破解思路MD5/SHA系列哈希签名最常见90%的网站用的都是这种只要找到拼接规则和密钥就能直接破解AES对称加密需要找到密钥和偏移量IV一般都在JS代码里硬编码RSA非对称加密用公钥加密这种一般只能抠JS代码运行或者找到公钥自己用Python实现加密自定义加密算法有些大厂会自己写加密算法这种就要耐心调试一步步分析逻辑webpack打包的JS现在很多网站的JS都是webpack打包的找函数会麻烦一点可以用AST工具解包或者找导出的模块避坑指南新手必看注意参数顺序签名生成的时候参数的顺序非常重要一定要和JS里的排序逻辑完全一致不然生成的sign肯定不对编码问题JS和Python的默认编码可能不一样中文、特殊字符要注意统一用UTF-8编码时间戳同步有些网站对时间戳的误差要求很严不要用本地时间最好先从服务器获取时间随机数规则有些网站的随机数nonce有格式要求比如长度、字符范围要和JS里生成的规则一致不要硬抠大段代码如果加密逻辑特别复杂不如直接用execjs跑抠出来的JS代码比自己重写省事还不容易错代码混淆应对遇到混淆后的JS代码用js-beautify格式化之后再看或者用AST工具还原变量名进阶玩法自动更新JS代码如果网站的加密算法经常变可以写个脚本自动拉取最新的JS代码提取加密函数RPC远程调用直接调用浏览器里的JS函数生成签名不用抠代码适合加密逻辑特别复杂的场景签名池多进程生成签名提高爬虫效率反混淆工具用AST工具自动还原混淆后的JS代码省掉手动分析的时间写在最后JS逆向和签名破解听起来很难其实核心就是找到加密函数然后复现逻辑大部分中小型网站的加密都非常简单花点时间都能搞定。我刚开始做JS逆向的时候一个简单的MD5签名都搞了一天现在一般的网站加密半小时就能搞定多练几次就熟了。上面的步骤都是我实战总结出来的跟着做就能破解大部分网站的签名验证有问题评论区留言。