尧图网站建设 尧图网络
  • 首页
  • 关于我们
  • 服务项目
  • 案例展示
  • 建站流程
  • 资讯中心
  • 联系我们
首页/资讯中心/详情

Python爬虫实战:逆向破解动态Cookie加密与签名生成

Python爬虫实战:逆向破解动态Cookie加密与签名生成
📅 发布时间:2026/6/19 17:31:09

1. 项目概述:当爬虫遇上动态加密的Cookie

做爬虫的朋友,尤其是和数据平台、电商网站、内容社区打交道比较多的,这两年肯定没少为“动态Cookie”头疼。你刚用requests或者scrapy写好一个脚本,美滋滋地跑起来,结果发现返回的数据要么是空的,要么直接给你弹一个“请先登录”或者“验证失败”的提示。回头一检查,问题往往就出在Cookie上——你手动从浏览器复制下来的那一串sessionid、token,在脚本里用不了几分钟就失效了,或者干脆从一开始就没用。

这就是典型的“动态Cookie加密”反爬策略。服务器不再傻傻地接受一个固定不变的Cookie字符串,而是要求客户端(通常是浏览器)在每次发起关键请求时,都携带一个经过特定JS算法实时计算、加密的Cookie值。这个值可能依赖于当前时间戳、页面上的某个动态变量、甚至是你上一次请求的响应内容。它的核心目的,就是让自动化脚本难以简单地“复制粘贴”来模拟登录或维持会话状态。

面对这种局面,传统的“硬编码Cookie”或“模拟登录后复用Session”的方法基本宣告失效。我们必须深入前端JavaScript的腹地,去搞清楚这个Cookie到底是怎么生成的,然后用Python把这个生成逻辑完整地复现出来。这个过程,就是“JS逆向分析与破解”。它不再是简单的HTTP请求模拟,而是一场与前端加密逻辑的直接对话。今天,我们就以一个虚构但极具代表性的“数据查询平台”为例,手把手带你走通从发现加密Cookie,到逆向分析JS,最终用Python实现动态生成的完整实战流程。无论你是爬虫新手遇到了第一个难啃的骨头,还是老手想系统化自己的逆向思路,这篇文章都能给你提供一套可直接套用的“作战地图”。

2. 核心思路与逆向分析框架拆解

在开始动手之前,我们先建立起一个清晰的逆向分析框架。盲目地扎进海量的JS代码里,很容易迷失方向。我的经验是,遵循“由外而内,由果溯因”的路径。

2.1 逆向分析的核心路径:抓包 -> 定位 -> 扣码 -> 还原

整个逆向过程可以标准化为四个步骤,我称之为“逆向四步法”:

  1. 抓包定位关键请求:使用浏览器开发者工具(F12),准确找到那个携带了加密Cookie、获取目标数据的核心请求。这是所有工作的起点,务必确认无误。
  2. 追踪加密参数生成位置:在找到的关键请求上,查看其Headers中的Cookie字段,找到那个可疑的、看起来像加密字符串的键值对(例如sign: a1b2c3d4e5f6...)。然后,通过“搜索”、“调用栈(Call Stack)”和“XHR/ Fetch断点”来定位生成这个值的JavaScript代码块。
  3. 分析与扣取核心JS代码:深入分析定位到的JS函数,理解其加密逻辑、依赖的变量和函数。将必要的函数代码“扣”出来,形成一个独立的、可分析的JS片段。这一步最考验耐心和细心。
  4. 使用Python复现加密逻辑:将扣取的JS代码逻辑,用Python重新实现。这通常涉及找到对应的加密库(如CryptoJS的Python版pycryptodome),并确保所有输入参数(时间戳、随机数、其他Cookie值等)的获取方式与浏览器环境一致。

这个框架是通用的,但具体到动态Cookie,有几个特别需要注意的点。

2.2 动态Cookie的常见加密模式与识别

动态Cookie的加密并非无迹可寻,通过观察,我们可以归纳出几种常见模式,这能帮助我们快速做出假设:

  • 时间戳签名型:Cookie值中嵌入了当前时间戳,并与其他固定或半固定值(如用户ID、固定字符串)进行哈希(如MD5、SHA256)或HMAC运算。这是最常见的一种。你可能会在Cookie里看到类似sign=md5(userId + timestamp)的形态。识别特征:Cookie值随时间变化,但变化有规律(如每秒一变),且长度固定(哈希值特征)。
  • 请求参数依赖型:Cookie的值依赖于本次请求的某些参数,比如请求体(POST data)的某个字段,或者URL中的某个查询参数。服务器通过验证Cookie是否与这些参数匹配来判断请求合法性。识别特征:改变请求参数,原来的Cookie立即失效,必须重新生成。
  • 链式反应型:最复杂的一种。第一个请求的响应中会包含一个“种子”值(可能藏在Set-Cookie、响应JSON或HTML的某个标签里),后续请求的加密Cookie需要基于这个“种子”值进行计算。识别特征:请求有明显的先后顺序,且前一个请求的响应内容是后一个请求的必要条件。

在我们接下来的实战案例中,遇到的是一个“时间戳签名型”和“链式反应型”的结合体,这也是目前中高级反爬中非常流行的方式,破解它极具教学意义。

注意:在开始任何逆向操作前,请务必确认你的行为符合目标网站的robots.txt协议,并尊重其服务条款。本技术分享仅用于学习交流与安全测试(在授权范围内),请勿用于任何非法或侵扰性用途。

3. 实战环境准备与工具链配置

工欲善其事,必先利其器。一套顺手的工具能极大提升逆向分析的效率和成功率。下面是我多年实战沉淀下来的“标配”工具链。

3.1 浏览器开发者工具:你的主战场

现代浏览器(Chrome/Edge/Firefox)的开发者工具是逆向分析的基石,必须熟练掌握以下几个面板:

  • 网络(Network):核心中的核心。记录所有网络请求。务必勾选“Preserve log”(保留日志)和“Disable cache”(禁用缓存),防止请求被清空或缓存干扰。学会使用筛选器(Filter),如XHR、JS、Doc来快速定位请求。
  • 源代码(Sources):用于调试JavaScript。可以设置断点、单步执行、查看调用栈、监控变量值。其中的“搜索”功能(Ctrl+Shift+F)可以全局搜索JS文件中的关键字(如Cookie名、加密函数名)。
  • 控制台(Console):可以执行任意JS代码,用于测试扣取出来的函数,或者查看全局变量。console.log()是调试的好帮手。
  • 应用(Application):查看和操作Cookie、本地存储(LocalStorage)、会话存储(SessionStorage)。有时加密种子会存在这里。

3.2 辅助分析工具:提升效率的利器

  • Node.js:很多时候,扣出来的JS代码需要在一个接近浏览器的环境中运行测试。安装Node.js后,你可以创建一个.js文件,用node命令来执行,验证函数逻辑是否正确,这比在浏览器控制台调试大段代码更方便。
  • Python环境及相关库:这是我们的最终实现端。
    • Requests:用于发送HTTP请求,毋庸置疑。
    • PyExecJS:一个非常关键的库。它允许你在Python中执行JavaScript代码。对于逻辑复杂但不想用Python重写的JS函数,这是一个“偷懒”但高效的选择。不过它依赖本地JS运行时(如Node.js)。
    • Pycryptodome或cryptography:Python下强大的加密解密库。当逆向发现使用的是AES、DES、RSA等标准算法时,用这些库实现比用PyExecJS调用原JS性能更好,也更稳定。
    • JSON:Python标准库,用于处理接口返回的JSON数据。

我的推荐是:分析阶段多用浏览器工具和Node.js进行验证,最终生产代码尽量用Python原生库(如Pycryptodome)重写核心加密逻辑,以获得更好的性能和可维护性。PyExecJS更适合作为过渡验证工具或处理极其复杂的、非标准混淆逻辑。

3.3 实战目标网站分析(模拟案例)

为了便于讲解,我们假设一个目标网站:https://data-platform.example.com。该网站需要登录后才能查询数据。登录后,查询核心数据的API接口为POST https://data-platform.example.com/api/v1/query。

我们遇到的现象:

  1. 使用账号密码正常登录后,可以手动在网页上查询数据。
  2. 使用Python脚本,携带登录后获取的静态Cookie(如session_id)去请求/api/v1/query接口,返回{"code": 403, "msg": "Invalid signature"}。
  3. 检查浏览器中成功请求的Headers,发现除了常见的session_id外,还有一个额外的Cookie:sign: 80f8d8e7b7d6c5b4a3f2e1d0c9b8a7f6。这个sign的值每次请求都在变化。
  4. 初步判断,sign是一个动态加密的Cookie,是服务端进行请求合法性校验的关键。

我们的任务就是破解这个sign的生成逻辑。

4. 逆向分析过程全实录:定位与解密sign的生成

现在,我们进入最核心的逆向分析环节。请打开浏览器的开发者工具,并访问我们的模拟目标网站。

4.1 第一步:抓包与关键请求定位

  1. 登录网站后,在数据查询页面进行一次查询操作。
  2. 打开开发者工具的Network面板,清空记录,然后点击查询按钮。
  3. 在请求列表中,找到类型为XHR或Fetch,且请求URL为/api/v1/query的那一条。这就是我们的关键请求。
  4. 点击这个请求,查看Headers选项卡。在Request Headers部分,找到Cookie这一行。你会看到类似这样的内容:
    Cookie: session_id=abc123def456; sign=80f8d8e7b7d6c5b4a3f2e1d0c9b8a7f6
    确认sign存在且其值是一串看起来像哈希的字符串。

4.2 第二步:追踪sign的生成位置

这是逆向中最需要技巧的一步。我们有几种武器:

方法A:全局搜索在Sources面板,按Ctrl+Shift+F打开全局搜索。因为sign是Cookie名,我们可以直接搜索字符串"sign"或'sign'(包括引号)。在结果中,寻找类似document.cookie = "sign=" + ...或者cookie: "sign=" + ...的赋值语句。这能快速定位到设置Cookie的代码。

方法B:XHR/Fetch断点(推荐)在Sources面板,找到右侧的XHR/Fetch Breakpoints区域。点击+号,输入包含/api/v1/query的部分URL字符串。这样,当任何JS代码发起向这个URL的请求时,执行就会自动暂停。此时,查看右侧的Call Stack(调用栈),它显示了暂停时代码的执行路径。从最上面(当前暂停的行)往下看,寻找可能包含sign计算逻辑的、属于网站自身域的JS文件(而不是jquery.min.js这类库文件)。点击这些栈帧,就能直接跳到对应的代码行。

方法C:事件监听器断点在Sources面板,找到右侧的Event Listener Breakpoints。展开Script类别,勾选Script First Statement。然后刷新页面或执行查询。这会在页面执行第一句JS时断住,然后你可以通过“单步执行”慢慢跟踪,但这种方法效率较低,适合代码量极小或前两种方法失效的情况。

在我们的模拟案例中,通过方法A搜索"sign",我们很快在一個名为app.xxxxxx.js的文件中找到了一段关键代码:

function generateSign(timestamp, secretKey) { var message = 'data_query_' + timestamp; var hash = CryptoJS.HmacSHA256(message, secretKey); return hash.toString(CryptoJS.enc.Hex); } // 在某处调用 var currentTime = Date.now(); var sKey = window._globalSecret || 'defaultSecret'; var signValue = generateSign(currentTime, sKey); document.cookie = "sign=" + signValue + "; path=/";

太好了!我们似乎直接找到了生成函数。但别急,这里面有坑。

4.3 第三步:深入分析与扣取核心代码

找到代码只是第一步,理解它并确保能完整复现才是关键。

  1. 分析函数逻辑:generateSign函数接受两个参数:timestamp和secretKey。它将字符串'data_query_'和timestamp拼接起来,然后用CryptoJS.HmacSHA256算法,以secretKey为密钥,计算消息认证码(HMAC),最后输出十六进制字符串。

  2. 寻找参数来源:

    • timestamp: 代码中直接使用Date.now(),这是当前时间的时间戳(毫秒级)。
    • secretKey: 代码中尝试从window._globalSecret获取,如果不存在则使用默认值'defaultSecret'。这是一个关键点!window._globalSecret这个变量是从哪里来的?我们需要找到它被赋值的地方。
  3. 追踪secretKey:在同一个JS文件或全局搜索_globalSecret。我们发现,在页面更早加载的一个初始化脚本里,有这样一段:

    // 从某个接口获取动态密钥 fetch('/api/v1/getSecret') .then(res => res.json()) .then(data => { window._globalSecret = data.secretKey; // 然后才触发后续的查询操作... });

    破案了!这是一个经典的“链式反应型”加密。secretKey并不是固定的,而是需要先请求一个/api/v1/getSecret接口来获取。获取到的secretKey可能有一定有效期。之后生成sign时,需要用到这个动态的secretKey和当前的timestamp。

  4. 扣取代码:我们需要扣取两部分。

    • 第一部分:调用/api/v1/getSecret获取secretKey的逻辑(通常很简单,就是一个GET请求)。
    • 第二部分:上面的generateSign函数,以及它依赖的CryptoJS库。在Node.js或PyExecJS环境中,我们需要确保CryptoJS可用。

    扣取后的完整JS测试片段(Node.js环境):

    // 假设我们已经通过其他方式拿到了 secretKey 和 timestamp var CryptoJS = require("crypto-js"); // 需要先安装 npm install crypto-js function generateSign(timestamp, secretKey) { var message = 'data_query_' + timestamp; var hash = CryptoJS.HmacSHA256(message, secretKey); return hash.toString(CryptoJS.enc.Hex); } // 测试 var testTimestamp = 1685432100000; var testSecretKey = 'dynamic_secret_from_api_123'; var testSign = generateSign(testTimestamp, testSecretKey); console.log('Generated Sign:', testSign);

    在Node中运行这段代码,如果能输出一个64位的十六进制字符串,说明扣取的逻辑是通的。

4.4 第四步:Python复现加密逻辑

现在,我们将JS逻辑转化为Python。这里有两种选择:

方案一:使用PyExecJS(快速验证)

import execjs import time import requests # 1. 获取动态 secretKey session = requests.Session() # 先模拟登录或其他必要步骤,获取基础session_id... # login_response = session.post('/login', data={...}) secret_response = session.get('https://data-platform.example.com/api/v1/getSecret') secret_data = secret_response.json() dynamic_secret_key = secret_data['secretKey'] # 2. 使用PyExecJS执行JS函数 with open('generate_sign.js', 'r', encoding='utf-8') as f: js_code = f.read() ctx = execjs.compile(js_code) # generate_sign.js 里是扣取的包含generateSign函数的代码 current_timestamp = int(time.time() * 1000) # 毫秒级时间戳,与Date.now()对齐 sign_value = ctx.call('generateSign', current_timestamp, dynamic_secret_key) print(f"生成的sign: {sign_value}") # 3. 构造最终请求 cookies = { 'session_id': 'your_session_id_here', # 从登录后的session中获取 'sign': sign_value } headers = { 'User-Agent': 'Mozilla/5.0 ...', # ... 其他必要headers } query_data = {...} # 你的查询参数 response = session.post('https://data-platform.example.com/api/v1/query', json=query_data, headers=headers, cookies=cookies) print(response.json())

方案二:使用Python原生加密库(推荐用于生产)CryptoJS.HmacSHA256对应Python的hmac库 +hashlib。

import hmac import hashlib import time import requests def generate_sign_py(timestamp: int, secret_key: str) -> str: """ 用Python实现HMAC-SHA256签名 """ message = f'data_query_{timestamp}'.encode('utf-8') key = secret_key.encode('utf-8') # 使用hmac库 signature = hmac.new(key, message, hashlib.sha256) return signature.hexdigest() # 使用方式 session = requests.Session() # ... 获取dynamic_secret_key ... current_timestamp = int(time.time() * 1000) sign_value = generate_sign_py(current_timestamp, dynamic_secret_key) # ... 后续请求构造与方案一相同

两种方案对比:

  • PyExecJS:优势是对于极其复杂或混淆严重的JS逻辑,可以几乎无脑移植,适合快速破解和验证。劣势是性能较差(需要启动JS运行时),且依赖外部环境。
  • Python原生库:优势是性能好,不依赖外部环境,代码更干净。劣势是需要准确理解JS中的加密算法和数据处理方式(如字符串编码、字节处理等),并找到对应的Python实现。

实操心得:在真实项目中,我优先尝试用Python原生库重写。只有遇到那些做了大量自定义变形、或者严重混淆导致难以直接理解算法的情况,才会退而求其次使用PyExecJS。用Python重写的过程,也是真正理解加密逻辑的过程。

5. 完整爬虫架构设计与代码实现

破解了加密算法,我们还需要将其融入一个健壮、可维护的爬虫架构中。不能每次请求都临时去算,要考虑密钥管理、时效性、错误重试等问题。

5.1 爬虫类结构设计

下面是一个面向对象的爬虫类设计,它封装了动态Cookie管理的逻辑:

import time import requests import hashlib import hmac from typing import Optional, Dict, Any from datetime import datetime, timedelta class DynamicCookieSpider: def __init__(self, base_url: str, login_info: Dict[str, str]): self.base_url = base_url.rstrip('/') self.session = requests.Session() self.session.headers.update({ 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 ...', 'Accept': 'application/json, text/plain, */*', 'Accept-Language': 'zh-CN,zh;q=0.9', }) # 状态管理 self._secret_key: Optional[str] = None self._secret_key_expiry: Optional[datetime] = None # 假设密钥有过期时间 self._logged_in = False # 登录 self._login(login_info) def _login(self, login_info: Dict[str, str]): """模拟登录,获取基础会话Cookie(如session_id)""" login_url = f"{self.base_url}/login" # 这里需要根据实际网站修改登录参数和解析逻辑 resp = self.session.post(login_url, data=login_info) resp.raise_for_status() # 假设登录成功会在响应中返回或通过Set-Cookie设置session_id # 这里简单判断,实际需要更严谨的检查 if 'session_id' in self.session.cookies.get_dict(): self._logged_in = True print("登录成功") else: raise Exception("登录失败,未获取到session_id") def _get_secret_key(self) -> str: """获取动态密钥,并管理其缓存和过期""" # 如果密钥存在且未过期,直接返回 if self._secret_key and self._secret_key_expiry and datetime.now() < self._secret_key_expiry: return self._secret_key # 否则,重新获取 secret_url = f"{self.base_url}/api/v1/getSecret" resp = self.session.get(secret_url) resp.raise_for_status() data = resp.json() self._secret_key = data['secretKey'] # 假设接口返回了expiresIn字段,单位秒 expires_in = data.get('expiresIn', 300) # 默认5分钟 self._secret_key_expiry = datetime.now() + timedelta(seconds=expires_in - 30) # 提前30秒过期,留出缓冲 print(f"已获取新的动态密钥,有效期至: {self._secret_key_expiry}") return self._secret_key @staticmethod def _generate_sign(timestamp: int, secret_key: str) -> str: """生成签名(HMAC-SHA256)""" message = f'data_query_{timestamp}'.encode('utf-8') key = secret_key.encode('utf-8') signature = hmac.new(key, message, hashlib.sha256) return signature.hexdigest() def _get_signed_cookies(self) -> Dict[str, str]: """生成包含动态sign的完整Cookie字典""" if not self._logged_in: raise Exception("请先登录") base_cookies = self.session.cookies.get_dict() # 获取已有的cookies,如session_id secret_key = self._get_secret_key() current_timestamp = int(time.time() * 1000) # 毫秒级 sign_value = self._generate_sign(current_timestamp, secret_key) # 将新的sign加入Cookie字典 signed_cookies = base_cookies.copy() signed_cookies['sign'] = sign_value return signed_cookies def query_data(self, query_params: Dict[str, Any]) -> Dict[str, Any]: """执行数据查询的核心方法""" query_url = f"{self.base_url}/api/v1/query" # 获取带签名的Cookie cookies = self._get_signed_cookies() # 构造请求 try: resp = self.session.post(query_url, json=query_params, cookies=cookies) resp.raise_for_status() return resp.json() except requests.exceptions.HTTPError as e: # 如果是签名失效(如403),可以尝试刷新密钥重试一次 if e.response.status_code == 403: print("签名可能失效,尝试刷新密钥并重试...") self._secret_key = None # 强制清除旧密钥 cookies = self._get_signed_cookies() # 重新获取 resp = self.session.post(query_url, json=query_params, cookies=cookies) resp.raise_for_status() return resp.json() else: raise e # 使用示例 if __name__ == '__main__': spider = DynamicCookieSpider( base_url='https://data-platform.example.com', login_info={'username': 'your_user', 'password': 'your_pass'} ) result = spider.query_data({'page': 1, 'size': 20}) print("查询结果:", result)

5.2 关键组件解析:会话、密钥与签名管理

这个设计中有几个关键点,确保了爬虫的稳定性:

  1. 会话(Session)管理:使用requests.Session()对象,它会自动处理登录后服务器通过Set-Cookie下发的session_id等基础Cookie,并在后续请求中自动携带。这省去了我们手动解析和传递的麻烦。

  2. 动态密钥的生命周期管理:_get_secret_key方法实现了简单的缓存和过期逻辑。它检查内存中保存的密钥是否在有效期内,如果是则直接使用,避免了对/getSecret接口的频繁请求。过期时间根据接口返回的expiresIn计算,并设置了30秒的缓冲提前量,防止在请求中途密钥过期。

  3. 签名生成与Cookie组装:_get_signed_cookies方法负责整个流程的串联:获取密钥 -> 获取当前时间戳 -> 生成签名 -> 组装最终Cookie字典。这样,业务方法query_data只需要调用这一个方法就能拿到有效的Cookie。

  4. 错误处理与重试机制:在query_data方法中,捕获了HTTP 403错误(签名无效)。当发生这种错误时,程序会尝试清除旧的secret_key,重新获取并生成签名后重试一次请求。这是一种针对动态密钥意外失效的容错机制。

注意事项:这个架构假设/getSecret接口的调用本身不需要动态签名。如果连获取密钥的接口也需要签名,那就形成了“蛋生鸡,鸡生蛋”的问题。这种情况下,通常第一个签名会使用一个硬编码在JS中的固定密钥或更简单的算法来生成。你需要逆向分析获取密钥的那个请求,其签名生成逻辑可能不同。

6. 逆向过程中的常见问题与深度排查技巧

即使按照上述流程,你也可能会遇到各种棘手的情况。下面是我踩过无数坑后总结的排查清单。

6.1 问题一:全局搜索不到关键词

  • 可能原因:代码被混淆(Obfuscation)或压缩(Minify)。变量名、函数名被替换成了a, b, c, _0x1a2b3c等形式。
  • 解决方案:
    1. 使用XHR/Fetch断点:这是对付混淆代码最有效的方法。断住后,在调用栈里找,即使函数名是a,你也可以通过其上下文(比如它操作cookie对象)来判断。
    2. 搜索常量字符串:加密过程中往往会拼接一些常量字符串,比如例子中的'data_query_'。尝试搜索这些可能的常量片段。
    3. Hook大法:在Console中注入代码,拦截关键API。例如,拦截document.cookie的setter:
      var originalCookieDesc = Object.getOwnPropertyDescriptor(Document.prototype, 'cookie'); Object.defineProperty(document, 'cookie', { set: function(val) { console.trace('Cookie being set:', val); // 打印调用栈 debugger; // 自动断点 return originalCookieDesc.set.call(this, val); }, get: originalCookieDesc.get });
      当JS代码尝试设置Cookie时,会自动触发断点并打印堆栈。

6.2 问题二:扣取的JS代码在Node/Python中运行结果不对

  • 可能原因1:环境依赖缺失。浏览器全局对象(如window,document,navigator)在Node中不存在。JS代码可能使用了这些对象下的属性。
  • 排查:在Node中运行扣取的代码,查看报错信息。常见的如window is not defined。
  • 解决:在JS代码执行前,补全这些全局变量。可以创建一个简单的模拟环境:
    // 在扣取的JS代码前加上 if (typeof window === 'undefined') { global.window = {}; global.document = {}; // 或者使用更完整的模拟库如 `jsdom` }
  • 可能原因2:加密库版本或实现差异。CryptoJS有很多版本和构建方式,不同版本间可能有细微差别。
  • 排查:对比浏览器中CryptoJS对象的版本和引入方式,与你本地Node模块的版本是否一致。
  • 解决:尽量使用和网页相同版本的CryptoJS库。或者,彻底放弃使用CryptoJS,直接分析其算法并用Python原生实现(如HMAC-SHA256),这是最根本的解决之道。

6.3 问题三:生成的签名长度或格式不符

  • 可能原因1:编码问题。JS和Python对字符串的处理可能有差异。JS的CryptoJS.HmacSHA256默认接受字符串或WordArray,而Python的hmac.new需要bytes。
  • 排查与解决:确保在Python中,message和key都正确编码为bytes。例如,'data_query_123'在JS中是一个UTF-16字符串?通常我们按UTF-8处理。在Python中明确指定:message.encode('utf-8')。
  • 可能原因2:时间戳格式不一致。Date.now()返回13位毫秒时间戳。Python的int(time.time() * 1000)也是13位。但有些网站可能使用10位的秒级时间戳,或者对时间戳做了其他处理(如除以1000取整)。
  • 排查:在浏览器生成签名时,将timestamp变量console.log出来。然后在Python脚本里,打印出你用来计算签名的时间戳,对比两者是否完全一致。
  • 可能原因3:消息(message)拼接格式有误。可能不是简单的字符串拼接,中间可能有特殊分隔符(如冒号、换行符),或者拼接顺序不同(如secret + timestamp)。
  • 排查:在JS代码计算签名前,将最终用于计算HMAC的message字符串打印出来。然后在Python中,确保你组装的message字节序列与其完全一致,包括每一个字符和空格。

6.4 问题四:密钥(secretKey)获取请求也有签名

这是更复杂的情况,形成了依赖链。通常的破解思路是:

  1. 找到链条的起点:第一个不需要动态签名的请求是什么?往往是登录请求或者首页加载时的一个初始化请求。这个请求的响应里可能包含了一个用于后续签名的“初始密钥”或“种子”。
  2. 逆向第一层签名:分析获取secretKey的那个请求(比如/api/v1/getSecret)的签名是如何生成的。这个签名算法通常会简单一些,可能只依赖于一个固定值或者从第一步获取的“种子”。
  3. 分层实现:在你的爬虫代码中,按顺序实现:获取种子 -> 生成第一层签名获取动态密钥 -> 用动态密钥生成第二层签名用于业务请求。

6.5 高级技巧:自动化与RPC调试

对于需要反复调试的复杂JS,可以借助一些高级工具:

  • 使用puppeteer或playwright:这类无头浏览器自动化工具,可以让你用代码完全控制浏览器,执行JS,并获取结果。你可以将定位到的关键JS函数在页面上下文中直接执行,并拿到返回值,非常方便验证。
  • RPC(远程过程调用)调试:这是一个高阶技巧。通过在目标网页中注入一个JS脚本,创建一个WebSocket服务器,暴露关键的加密函数。然后你的Python程序可以通过WebSocket连接,直接调用这个函数并传入参数,得到计算结果。这相当于在浏览器原生环境中执行加密,100%还原,彻底绕过环境差异问题。不过实现起来有一定复杂度。

逆向分析是一个需要耐心、细心和逻辑推理的过程。每一个错误提示(如Invalid signature)都是线索。多对比、多打印、多假设、多验证,从最简单的假设开始测试,逐步逼近真相。当你成功用自己的Python代码生成出那个能够骗过服务器的sign值时,那种成就感是无与伦比的。记住,思路和框架比死记硬背代码更重要。掌握了这套“逆向四步法”和问题排查技巧,你就有能力去面对大多数动态Cookie加密的挑战了。

相关新闻

  • 个人跨省寄快递怎么省钱?2026长途低价渠道实测对比 - 快递物流资讯
  • 2026重庆闲置奢包回收测评|爱马仕LV香奈儿变现优选榜单 - 名奢变现站
  • 金价高位变现测评,哈尔滨黄金回收哪家贴合大盘价无隐形收费 - 奢侈品交易观察员

最新新闻

  • 3种方法实现本地语音识别:让whisper.cpp成为你的私人语音助手
  • 2026 常州各区黄金回收行情对比,全城统一标价,收的顶区域无差价更公道 - 奢侈品回收测评
  • 为什么一个标签页崩溃不会让整个浏览器卡死?——聊聊浏览器的多进程架构
  • 2026成都黄金回收完整实操攻略|行情研判、计价方式、交易流程一站式汇总 - 奢侈品回收评测
  • 2026深圳黄金回收流程攻略,多家门店横向对比哪家更划算 - 奢侈品回收测评
  • 深入解析PF0200Z PMIC:为i.MX 6系统设计高效可靠的电源管理方案

日新闻

  • 5分钟掌握Python进化算法:Geatpy高性能优化工具完全指南
  • Microchip 24AA044 EEPROM选型与应用全指南:从参数解析到实战编程
  • 华为的鸿蒙到底有多牛?为什么称作遥遥领先?

周新闻

  • 3步解锁iOS设备:applera1n激活锁绕过完全指南
  • 39 2026 人工智能证书终极盘点,普通人选 AI 证书可以从这些方向入手
  • Redis 暴露公网有多危险?从端口检查到补救步骤

月新闻

  • 【总结】入门篇:50句话让你记住架构核心概念
  • WeChatMsg技术方案解析:实现Mac微信数据自主管理的完整解决方案
  • WeChatMsg:革新性微信数据备份方案,打造你的专属数字记忆库

关于尧图

  • 公司简介
  • 团队介绍
  • 企业文化
  • 荣誉资质

服务项目

  • 定制开发
  • 电商建站
  • UI 设计
  • 运维服务

快速链接

  • 案例展示
  • 建站流程
  • 常见问题
  • 资讯中心

联系方式

  • 📍北京市朝阳区互联网产业园 A 座 10 层
  • 📞400-888-8888
  • ✉️contact@rkmt.cn
  • 🕐周一至周日 9:00-21:00

© 2024 北京尧图网络科技有限公司 版权所有 | 京 ICP 备 XXXXXXXX 号