本篇博客为大家开始着手分析 请求头 x-ca-key、x-ca-nonce、x-ca-signature 加密相关内容
目标站点在本文进行隐藏,如有需要,可直接联系
一般你能搜到这篇博客,代表你采集的站点使用类似加密。
请求头解密目录
- x-ca-key、x-ca-nonce、x-ca-signature 相关说明
- 寻找 x-ca-key、x-ca-nonce、x-ca-signature 加密位置
- 提取关键加密逻辑
- X-Ca-Key 解析
- x-ca-nonce 解析
- x-ca-signature 解析
- 总结篇
x-ca-key、x-ca-nonce、x-ca-signature 相关说明
x-ca-key
、x-ca-nonce
、x-ca-signature
是一组用于进行身份验证和安全保护的 HTTP 请求头。
简易说明如下所示:
x-ca-key
:是用于标识请求发送者身份的字符串,一般是由服务提供商分配给每个客户端的唯一字符串。x-ca-nonce
:是一个随机字符串,用于防止重放攻击。它通常是一个由客户端生成的随机数字或字符串。x-ca-signature
:是对请求内容的签名,用于验证请求的完整性和可信性。签名通常是通过使用私钥和一种哈希算法(如 SHA256)对请求内容进行计算得到的。
如果请求头中出现这三个参数,放心,是为了反爬用的,当然也可以用于限制请求频率,防止恶意攻击。
在解决该反爬问题时,第一步就是要找到他们的加密点。
寻找 x-ca-key、x-ca-nonce、x-ca-signature 加密位置
这一步主要看你对开发者工具的使用熟练程度了,寻找任意一个携带该请求头参数的请求,然后添加相应断点。
通过请求地址中的部分关键字,即可添加 XHR 断点。
再次刷新页面,可进入断点中,一般会停留在 send()
函数位置。
下面的步骤就是比较枯燥的了,需要一点点的解密,例如在本函数头部找到 headers
,发现其参数 x-ca-key、x-ca-nonce、x-ca-signature 已经被赋值。
不着急,在调用堆栈中寻找能看懂名称的函数,例如这里的 getList
,然后在对应函数中下新断点。
再次刷新页面,又一次进入断点函数,这里需要用到开发者工具的调试工具。
优先使用 跳过下一函数调用,过程中还需要不断检查参数的最新状态。
反复调试和阅读上下文代码,找到与标题相同的关键字
具体如下:
这里出现了 x-ca-key、x-ca-nonce、x-ca-signature 三个关键字的赋值,可以初步判断其为重点加密逻辑。
这里继续新增一个断点,以便后续使用。
提取关键加密逻辑
在找到加密位置之后,对其解读就成为了后续重点内容。
X-Ca-Key 解析
在 JS 代码中,看到 e.headers["X-Ca-Key"] = s,
代码段,该值为变量 s
的值,s
等于一段数字,我们需要在前文找到其逻辑来源。
结果也比较简单,s
是一个固定值:
(s = 203899271), (r = "bK9jk5dBEtjauy6gXL7vZCPJ1fOy076H");
x-ca-nonce 解析
依旧是先看 JS 代码,其赋值位置是一个 p()
函数。
e.headers["X-Ca-Nonce"] = p(),
鼠标移动到 p()
函数上可以得到函数详情连接,点击跳转去提取逻辑。
找到 p()
函数对应代码如下,这里需要一点点 JS 知识了。
p = function(e) {
var t = e || null;
return null == t && (t = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (function(e) {
var t = 16 * Math.random() | 0;
return ("x" === e ? t : 3 & t | 8).toString(16)
}
))),
这是 JavaScript 中生成 UUID (Universally Unique Identifier) 的代码,UUID 是一种用来在应用程序或者网络中标识资源的字符串。
其中,“xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx” 是一个由 36 个字符组成的字符串,是 UUID 的格式。
.replace(/[xy]/g 是一个正则表达式,它替换字符串中的所有 “x” 和 “y” 为随机生成的字符,而后面的函数就是生成随机字符的逻辑,e
是一个随机的 16 进制的数字。
后续我们需要用 Python 改写这段代码。
x-ca-signature 解析
这是本案例中的重点内容了,签名计算,提取相关 JS 代码。
e.headers["X-Ca-Signature"] = m({
method: a,
url: l,
accept: t,
params: d,
date: n,
contentType: i,
headers: e.headers,
appSecret: r
}),
可以看到,其核心使用的是 m()
函数,将其余值作为对象进行了传入,其中 appSecret: r
中的 r
,在前文获取 x-ca-key
的时候已经得到。
跳转到 m()
函数内容,取得关键逻辑,后续的难点就是解读下面这段代码。
m = function (e) {
var t = e.method,
n = e.url,
o = e.appSecret,
i = e.accept,
s = e.date,
r = e.contentType,
a = e.params,
d = e.headers,
p = "";
a || -1 === n.indexOf("?")
? a || (a = {})
: ((a = (function (e) {
var t = {},
n = e.match(/[?&]([^=&#]+)=([^&#]*)/g);
if (n)
for (var o in n) {
var i = n[o].split("="),
s = i[0].substr(1),
r = i[1];
t[s] ? (t[s] = [].concat(t[s], r)) : (t[s] = r);
}
return t;
})(n)),
(n = n.split("?")[0]));
(p += "".concat(t, "\n")),
(p += "".concat(i, "\n")),
(p += "".concat("", "\n")),
(p += "".concat(r, "\n")),
(p += "".concat(s, "\n"));
var m,
f = u(d),
h = c(Array.from(Object.keys(f)).sort());
try {
for (h.s(); !(m = h.n()).done; ) {
var v = m.value;
p += v + ":" + f[v] + "\n";
}
} catch (e) {
h.e(e);
} finally {
h.f();
}
return (
(p += (function (e, t) {
var n,
o = null,
i = c(Array.from(Object.keys(t)).sort());
try {
for (i.s(); !(n = i.n()).done; ) {
var s = n.value,
r = void 0;
null !== t[s] &&
void 0 !== t[s] &&
((r = "" !== t[s] ? s + "=" + t[s] : s + t[s]),
(o = o ? o + "&" + r : r));
}
} catch (e) {
i.e(e);
} finally {
i.f();
}
return o ? e + "?" + o : e;
})(
n.replace(
/^(?=^.{3,255}$)(http(s)?:\/\/)?(www\.)?[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(\.csdn\.net)/,
""
),
a
)),
l.a.HmacSHA256(p, o).toString(l.a.enc.Base64)
);
};
总结篇
详细的解释,我们留到下一篇博客继续讲解,不要忘记点赞,收藏,评论
📢📢📢📢📢📢
💗 你正在阅读 【梦想橡皮擦】 的博客
👍 阅读完毕,可以点点小手赞一下
🌻 发现错误,直接评论区中指正吧
📆 橡皮擦的第 850 篇原创博客
全网 6000+人正在学习的 爬虫专栏 👇👇👇👇
- ⭐️ Python 爬虫 120,点击订购 ⭐️
- ⭐️ 爬虫 100 例教程,点击订购 ⭐️