之前接了个需求,对方甩过来一个数据平台的接口文档,接口本身不复杂,但请求参数里有个字段一眼就让人皱眉——k。一个每次请求都会变化、看起来毫无规律的字符串,长度固定,既不像时间戳也不像简单的MD5。这种场景做爬虫的朋友应该都不陌生:参数加密,而且是前端JS动态生成的那种。调试了半天,最后发现所有的逻辑都藏在前端打包压缩过的JS里,不把这段逻辑逆清楚,你连一个合法的请求都拼不出来。
这篇文章我就用这个案例,把整个逆向过程从头到尾理一遍。从怎么在浏览器里定位k参数的生成位置,到怎么把混淆过的JS函数提炼出来,再到最后用Python原生实现同一套加密逻辑。全程不绕弯子,你跟着操作能复现出来就行。会涉及不少实操细节和踩坑记录,爬虫新手和做JS逆向有段时间的朋友应该都能用得上。
先说清楚边界:本篇所有分析基于授权测试环境,逆向的目的在于接口联调和数据获取流程的自动化优化。大家在做类似事情之前,自己先确认目标平台的服务条款和授权范围,这是底线。
1. 参数定位:从HTTP请求到JS执行现场
1.1 先把请求特征固定下来
打开目标网站的页面,按F12进入开发者工具,切到Network面板,刷新页面触发数据请求。会看到页面底部列表加载时发出了一堆XHR请求,其中一条就是我们需要的核心数据接口。
把这条请求点开,看几个关键位置:
- 请求URL里有没有额外的query参数,比如
?page=1&size=20&k=xxxx - 请求体(Payload或Request Body)里是JSON、FormData还是普通键值对
- 请求头和响应头里有没有特殊的自定义字段
以这个案例为例,请求体是典型的JSON格式,里面除了业务字段之外,就有那个k。点开Payload视图会看到类似这样的结构:
json复制{
"page": 1,
"limit": 20,
"k": "3f6b7f0e0b6a4b4b8f7f8e3a4c9d0e2f",
"timestamp": 1736000000000
}
注意几个细节:k的长度是32位,而且是十六进制字符,纯小写。这意味着它大概率是一次MD5或类似哈希运算的输出,或者是某种自定义加密后的十六进制表示。另外就是timestamp字段,这个网站把时间戳放在JSON里,说明后端会校验时间范围,时间偏差太大的请求直接拒绝。
这里有个通用经验:第一步别急着找加密逻辑,先把请求参数的结构、类型、长度这些特征记录下来。为什么?因为参数特征本身就给出了大量线索。32位hex基本可以锁定MD5家族,32位Base64可能是AES后编码,40位hex可能是SHA1,64位hex大概率SHA256。你心里有底了,后面断点调试时就有方向。
1.2 全局搜索:最快的定位方式
请求参数已经确认,下一步就是在JS源码里找到k的生成逻辑。
切到Sources面板,按Ctrl+Shift+F进行全局搜索,输入k:(带冒号,因为JSON里键值对的写法通常是k:或者"k":),或者直接搜k =、k: getK这类模式。全局搜索可能会命中几百个结果,因为字母k太常见了,这时候要缩小范围。
我一般会加几个过滤条件:
- 只看.js文件,排除html、css
- 搜索
k:并配合"k"来筛选,能直接命中对象字面量写法 - 搜索
timestamp加k,如果加密逻辑中同时出现了这两个字段,大概率是在同一个函数里做的
实际操作中,我是在一个叫chunk-xxxx.js的大文件里命中的。这个文件是典型的webpack打包产物,总大小超过1MB,所有代码都被压缩成一行,正常情况下根本没法直接阅读。但压缩归压缩,函数名、字符串常量还在,关键逻辑是可以搜索到的。
如果全局搜索迟迟找不到突破口,可以换个思路:对XHR/fetch请求打断点。在Sources面板右侧的XHR/fetch breakpoints里点击加号,输入请求URL中包含的路径片段,比如api/list,然后刷新页面。当浏览器准备发起这个请求时,会暂停在XMLHttpRequest.send或fetch调用处,右侧Call Stack会显示当前调用链,往回一层层翻,就能找到构造参数的那段代码。这个方法比全局搜索更精准,适合在代码量大、变量名被压缩的场景下使用。
1.3 调用栈回溯:从网络请求倒推函数链
在XHR断点断下后,右侧的Call Stack面板会列出当前执行所在的函数调用序列:
text复制setRequestHeader / send (XMLHttpRequest)
sendRequest (webpack://xxx/./src/api/request.js)
listQuery (webpack://xxx/./src/api/module.js)
handleSearch (webpack://xxx/./src/views/xxx.vue)
往上翻,从listQuery到getK这一链,最终会看到一个关键函数,它做了类似的事情:
javascript复制
