在渗透测试过程中,我们常常需要通过修改前端传入的参数来探测目标系统的潜在漏洞。然而,许多网站设置了签名验证机制,一旦检测到参数被篡改,便会返回签名失效的错误,从而阻断测试流程。
但JS逆向的魅力正在于此:只要是前端发起请求所携带的参数,无论其采用何种加密方式,其加密逻辑必然存在于前端的代码之中。只要我们能找到并理解这段逻辑,就可以编写对应的脚本进行伪造,从而实现绕过签名或加密的防御。
下面,我将结合一次测试中遇到签名验证的实际案例,详细演示如何在前端定位并逆向其加密实现。

首先,我们需要定位签名生成代码的位置。关键字的选择至关重要。在这个例子中,签名参数sign非常常见,直接搜索会得到大量无关结果,效率低下。但参数noncestr相对少见,我们可以利用它作为突破口,间接定位到sign的生成位置。
在全局范围内搜索noncestr,发现只有一个匹配项,这极大地提高了定位效率。接下来,在其周围代码中寻找sign值的生成逻辑。

果然,在其附近就出现了sign。通常这类参数会被一起封装,只要找到一个,其他相关参数也大概率在附近。

定位到关键位置后,我们在此处设置断点进行调试。可以看到,一个变量f被传入某个函数,经过处理后生成了签名值sign。

我们在控制台打印一下,看看变量f到底是什么。

接下来,我们跟进生成签名的方法s(),分析其内部逻辑。

该函数的代码逻辑如下:
c = function(e, n)
{
var r = t.wordsToBytes(s(e));
return n && n.asBytes ? r : n && n.asString ? i.bytesToString(r) : t.bytesToHex(r)
}
当程序执行完这个方法后,sign值便生成了。

在控制台直接调用s()函数并输出结果。

可以看到,s()方法就是sign值的最终生成方法。我们尝试修改传入的参数,发现它能生成对应的新签名值。这意味着我们已经掌握了伪造任意sign值的能力。

将我们生成的新签名值填入数据包中重新发送,成功绕过了签名验证。

如果手动还原代码逻辑感觉繁琐,我们可以将找到的加密代码直接交给AI进行分析,让其帮我们写出等价的加密脚本。这是提高JavaScript逆向效率的一个实用技巧。
以下是核心的签名生成函数 f 和 s 的逻辑:
f = function() {
var e = arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : {}
, n = arguments.length > 1 && void 0 !== arguments[1] ? arguments[1] : u
, r = arguments.length > 2 && void 0 !== arguments[2] && arguments[2]
, o = t({}, e)
, i = {
appSignKey: n
}
, a = Date.now();
e.serverTimestamp && !l && (l = e.serverTimestamp - a),
o.timestamp = a + l,
o.noncestr = c(8, "number");
var f = Object.keys(o).filter((function(e) {
return void 0 != o[e] && "" !== o[e] || (delete o[e],
!1)
}
)).concat("appSignKey").sort().map((function(e) {
var t = i[e] || (void 0 == o[e] ? "" : o[e]);
return "".concat(e, "=").concat(t)
}
)).join("&");
return r && console.log("sign", e, f),
o.sign = s(f),
o
}
//下面是s()实现
function(e, n) {
var r = t.wordsToBytes(s(e));
return n && n.asBytes ? r : n && n.asString ? i.bytesToString(r) : t.bytesToHex(r)
}
AI分析后,可以给出等价的Python实现脚本:

Python脚本的核心代码如下:
import hashlib
def generate_sign(sign_str):
"""
核心SHA1加密方法 (还原JS里的s()函数)
:param sign_str: 拼接好的待签名原始字符串
:return: 小写的sha1签名结果,和JS完全一致
"""
sha1 = hashlib.sha1()
# 转bytes格式,utf-8编码,JS默认也是该编码,无乱码问题
sha1.update(sign_str.encode('utf-8'))
# 生成16进制小写字符串,完美还原 JS bytesToHex + 转小写
return sha1.hexdigest()
# -------------------------- 你提供的【正确待签名串】 --------------------------
raw_str = "appSignKey=4bTogwpz7RzNO2VTFtW7zcfRkAE97ox6ZSgcQi7FgYdqrHqKB7aGqEZ4o7yssa2aEXoV3bQwh12FFgVNlpyYk2Yjm9d2EZGeGu3&noncestr=48025550×tamp=1768236168865"
# 生成签名
final_sign = generate_sign(raw_str)
# 打印结果
print("最终生成的sign签名值:", final_sign)
运行脚本后,成功生成与前端一致的签名。

本文旨在分享一种在Web安全测试中定位前端加密逻辑的思路与技巧,并结合现代工具提升效率。具体的加密案例或许各有不同,但这种从非常见参数切入、动态调试分析、并利用工具辅助还原的思路,在技术社区如云栈社区的攻防讨论中具有通用性。
