Python爬虫必学之JS逆向:2025一品威客登录签名Signature解密

目录

前言

一、初步抓包分析

二、参数分析

2.1 U参数分析

2.2 M参数分析

2.3 加密函数分析

三、加密实现

四、Signature获取代码实现

五、实现总结


前言

        Signature(签名)是一种通过特定算法生成的数据校验值,通常叫签名,在Web安全中扮演着重要角色。其主要功能包括:

1. 数据完整性验证:确保请求数据在传输过程中未被篡改
2. 身份认证:验证请求来源的合法性
3. 接口保护:防止未授权访问
4. 防重放攻击:通过时间戳和随机字符串确保请求的唯一性

        学习Signature的逆向分析有助于:

1. 安全技术研究:深入理解现代Web安全机制
2. 恶意行为分析:识别和防范潜在攻击
3. 技术创新:推动安全技术发展
4. 爬虫技术:理解反爬虫机制

一、初步抓包分析

        使用谷歌浏览器打开一品威客官网,按F12打开开发者工具开始分析,调试,抓包。点击密码登录,随便输出手机号和密码,点击登录。

        从开发者工具 的Network可以看到登录接口" https://www.epwk.com/api/epwk/v1/user/login "

        从请求头就能看到 Signature

         我们可以在Network中全局搜索Signature,看到第一个就是js源码中的计算实现

        或者在Sources中右键搜索全部文件中的Signature

         直接点击进去就能看到Signature的js源码实现

        通过抓包分析,发现登录请求中的sign参数是经过加密的。通过搜索关键词定位到加密逻辑:

U.Signature = Object(f.a)(U, M, l.j ? l.g : l.c);

二、参数分析

2.1 U参数分析

        往上溯源 U 的生成代码:

        分析参数U,函数里面传入了U作为第一个参数,U的字段如上所示,这里面App-IdNonceStrTimestemp都是需要分析的,其他都是固定的

    Timestemp的值是A,A的计算可以往上搜到,它作用应该是防止重放攻击

        至于应用标识"App-Id":l.j ? l.f : l.b,我们可以在js中下断点,再次登录运行到这个地方

         可以移动鼠标到变量上查看其值,也可以在开发者工具的Console项上,输入对应的变量查看其值

        我们可以看到l.j的值为false,那么他会一直是false的值吗,我们可以继续如下追踪到j的实现

        点击如上的get j 处的 js位置,就能跳到j的获取实现代码

         代码往下溯源就能看到 c 就是 j 的值

        看得出来,c的值是一个固定值false,所以我们接下来要看l.b的值是怎么算的

        往下溯源 h:

        上面可以看出l.b也是一个固定值。App-Id的值由此容易得到,由于l.j为false,故App-Id的值为l.b的值。Timestemp的值就是个时间戳。接下来我们看到

        NonceStr: "".concat(A).concat(Object(h.f)()),

        为了增加请求唯一性,NonceStr的变量计算稍微有点繁琐,由TimestempObject(h.f)()拼接而成。看Object(h.f)()是什么。

        同理点击直接跳到 h.f 实现之处,如下

        看得出来e的默认值是5,该函数本质上就是生成一个随机字符串并截取字符串中的第3位到第,3+e位,既然是个随机函数,所以该值取什么值都无所谓。最后看M的实现,在Console的变量获取处输入M,就直接能获取其值

        其中的username和password和code就是我们输入的账号密码和图片验证码。refer可以理解成一个固定值,不用变。

2.2 M参数分析

参数M就是我们填写的登陆信息,账号密码加上图形验证码。

2.3 加密函数分析

        加密过程涉及两个主要函数:

1. f函数:用于参数排序和拼接

2. f.a函数:实现MD5和AES加密

        首先来从 Object(f.a) 函数入手,看代码 U.Signature = Object(f.a)(U, M, l.j ? l.g : l.c);

        我们可以直接断点此处代码,直接如上面截图地方输入 f.a 就能看到其实现之处,鼠标点击即可跳转至实现代码如下

        看到函数中的变量都跟arguments有关,先看arguments是什么。

        arguments 字典:它是一个字典类型的数据结构,里面有三个键值对。其中第一个键值对的值是变量 U,第二个是变量 M,第三个是一个固定值。不过目前不清楚 arguments 字典的具体键名。变量 data 和 e:变量 data 和 e 可以较容易获取到,且可以看出 e 的值为 "a75846eb4ac490420ac63db46d2a03bf"。公式 n = e + f(data) + f(t) + e:此公式里包含未知的函数 f 以及未知变量 t。若要计算出 n 的值,就需要明确 f 函数的具体功能和 t 的值。

        变量 t 跟变量 U 是一致的。函数f的实现如下同理找到。

如果我们看不懂具体代码是什么意思,我们可以断点到此,获取结果输出看看。

        大概就是一个字符串的拼接。这些代码到时直接复制实现即可。最后看dv函数。

三、加密实现

加密过程分为两步:

1. MD5加密:使用d函数

2. AES加密:使用v函数

如下同理追溯代码实现

  d函数是一个md5算法,v算法是一个AES加密,只要知道了密钥、偏移量和加密模式即可。

        上图中显示的key和iv都是字节,想要看到字符串类型的话可以采用toString函数。

四、Signature获取代码实现

运行环境:
- 浏览器环境
- 依赖库:CryptoJS
- 测试方法:Chrome控制台

        把上面逆向实现的相关js代码合并如下:

js.js代码:

// 引入CryptoJS库
const Crypto = CryptoJS;

// 时间戳生成
A = parseInt((new Date).getTime() / 1e3);

// 请求头参数
var U = {
    "App-Ver": "",
    "Os-Ver": "",
    "Device-Ver": "",
    "Imei": "",
    "Access-Token": "",
    "Timestemp": A,
    "NonceStr": "".concat(A).concat("syst41"),
    "App-Id": "4ac490420ac63db4",
    "Device-Os": "web"
};

// 请求体参数
var M = {
    'code': 'as3e',
    'hdn_refer': '',
    'password': '123456',
    'username': '15915863654'
};

// 签名盐值
const SIGNATURE_SALT = "a75846eb4ac490420ac63db46d2a03bf";

// 参数排序函数
f = function(t) {
    var e = "";
    return Object.keys(t).sort().forEach((function(n) {
        e += n + ("object" === typeof t[n] ? JSON.stringify(t[n], (function(t, e) {
            return "number" == typeof e && (e = String(e)),
            e
        })).replace(/\//g, "\\/") : t[n])
    })), e
}

// MD5加密函数
d = function(data) {
    return Crypto.MD5(data).toString();
}

// AES加密配置
l = {
    key: Crypto.enc.Utf8.parse("fX@VyCQVvpdj8RCa"),
    iv: Crypto.enc.Utf8.parse(function (t) {
        for (var e = "", i = 0; i < t.length - 1; i += 2) {
            var n = parseInt(t[i] + "" + t[i + 1], 16);
            e += String.fromCharCode(n)
        }
        return e
    }("00000000000000000000000000000000"))
}

// AES加密函数
v = function(data) {
    return function(data) {
        return Crypto.AES.encrypt(data, l.key, {
            iv: l.iv,
            mode: Crypto.mode.CBC,
            padding: Crypto.pad.Pkcs7
        }).toString()
    }(data)
}

// 签名生成函数
h = function(t) {
    var data = arguments.length > 1 && void 0 !== arguments[1] ? arguments[1] : {}
    , e = arguments.length > 2 && void 0 !== arguments[2] ? arguments[2] : SIGNATURE_SALT
    , n = e + f(data) + f(t) + e;
    return n = d(n),
        n = v(n)
}

// 生成签名
console.log(h(U, M, SIGNATURE_SALT));

        这里没有另外安装js环境,只能用html在浏览器上打开运行获取了

<!DOCTYPE html>
<html>
<head>
    <title>Crypto Test</title>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/crypto-js.min.js"></script>
</head>
<body>
    <script src="js.js"></script>
</body>
</html>

        直接用谷歌浏览器打开,在开发者工具的Console看到输出值,就是Signature的值了

五、实现总结

整个签名生成过程:

1. 构造请求参数U和L

2. 对参数进行排序和拼接

3. 使用固定密钥进行MD5加密

4. 使用AES进行二次加密

5. 生成最终的signature参数

技术要点:
1. 参数排序确保一致性
2. 双重加密提高安全性
3. 时间戳防止重放攻击
4. 随机字符串增加复杂度

安全机制:
1. 验证码保护
2. 签名防篡改
3. 时间戳防重放
4. 双重加密防破解

        单独逆向Signature确实作用有限,因为还有验证码的限制,无法绕过完整的验证流程,不能实现自动化操作。但是如果一些网站首次登录没有验证码,那就可以实现多个账号批量自动登录,然后可以被人实施自动爬取数据。

        想要根据Signature实现自动化登录的,可以自行安装 Python 及相关库(execjsrequests),安装 Node.js 作为 JavaScript 运行时环境,编写相关python代码,携带各种关键参数以及请求头,通过其登录接口进行请求,验证其完整流程的正确性。

        这一节内容,我们也从中学到了服务器请求安全机制的实现,了解js部分逆向原理,熟悉了浏览器中js的调试操作。

评论 162
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

攻城狮7号

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值