爬虫请求参数签名算法逆向(md5、aes、rsa、sm2 全套)

在网络爬虫开发中,当面对带有签名验证的接口时,参数签名算法逆向是绕开反爬机制的核心步骤。网站通过 MD5、AES、RSA、SM2 等加密算法对请求参数进行签名,验证请求的合法性,防止恶意爬取。本文将从签名算法的核心原理出发,结合实战案例,系统讲解四大主流签名算法的逆向思路、工具使用和代码实现,帮助开发者快速突破签名验证壁垒。

一、签名算法逆向核心逻辑

1. 签名的本质与作用

请求参数签名是网站后端对前端请求的 "身份校验" 机制:前端将关键参数按固定规则排序、拼接后,通过加密算法生成唯一签名串(如 sign 参数),后端接收请求后执行相同逻辑计算签名,对比一致则认为请求合法。其核心作用包括:

  • 防止参数被篡改(如价格、页码等关键信息);
  • 验证请求来源(仅合法前端能生成正确签名);
  • 抵御重放攻击(部分签名包含时间戳、随机数)。

2. 签名算法逆向通用流程

无论哪种加密算法,逆向过程均可遵循以下五步流程:

  1. 定位签名参数:通过抓包工具(Fiddler、Charles、Chrome 开发者工具)分析请求,找到签名字段(常见名称:sign、signature、token);
  2. 追踪参数生成逻辑 :在前端源码(JS 为主)中搜索签名字段,通过断点调试、关键词检索(如 md5encryptsign)定位生成签名的核心函数;
  3. 解析算法细节:分析核心函数的参数处理规则(排序、拼接、加盐)、加密算法类型、密钥来源(硬编码、接口返回、本地存储);
  4. 模拟实现:用 Python 复现参数处理逻辑和加密算法,生成与前端一致的签名;
  5. 验证调试:将生成的签名代入请求,对比响应结果,排查参数处理、算法实现中的问题。

二、四大签名算法逆向实战(含 Python 实现)

(一)MD5 签名:最常见的轻量验证

1. 算法特性
  • 哈希算法(不可逆),输出 32 位(小写 / 大写)或 16 位(取中间 16 位)字符串;
  • 核心逻辑:参数排序 → 拼接字符串 → 加盐(salt)→ MD5 加密;
  • 典型场景:普通接口的参数校验(如电商商品列表、新闻资讯接口)。
2. 逆向步骤与案例
步骤 1:抓包分析请求

以某资讯接口为例,抓包得到请求参数:

plaintext

复制代码
GET /api/news?page=1&limit=10&timestamp=1699999999&sign=e10adc3949ba59abbe56e057f20f883e
  • 关键参数:page(页码)、limit(条数)、timestamp(时间戳)、sign(签名)。
步骤 2:定位签名生成函数

在前端 JS 源码中搜索 sign,找到核心函数:

javascript

运行

复制代码
function generateSign(params) {
  // 1. 按参数名 ASCII 升序排序
  const sortedKeys = Object.keys(params).sort();
  // 2. 拼接参数(key=value 形式)
  let signStr = '';
  sortedKeys.forEach(key => {
    if (params[key] !== '') signStr += `${key}=${params[key]}&`;
  });
  // 3. 加盐(固定 salt:abc123)
  signStr += 'salt=abc123';
  // 4. MD5 加密(32 位小写)
  return md5(signStr);
}
步骤 3:Python 模拟实现

python

运行

复制代码
import hashlib
import time

def generate_md5_sign(page=1, limit=10):
    # 1. 构造基础参数
    params = {
        'page': page,
        'limit': limit,
        'timestamp': int(time.time())  # 与前端一致的时间戳
    }
    # 2. 按参数名升序排序
    sorted_keys = sorted(params.keys())
    # 3. 拼接字符串(key=value&key=value)
    sign_str = ''
    for key in sorted_keys:
        sign_str += f"{key}={params[key]}&"
    # 4. 加盐(与前端一致的 salt)
    sign_str += 'salt=abc123'
    # 5. MD5 加密
    md5 = hashlib.md5()
    md5.update(sign_str.encode('utf-8'))
    params['sign'] = md5.hexdigest()  # 32 位小写
    return params

# 测试:生成请求参数
request_params = generate_md5_sign(page=1, limit=10)
print(request_params)
# 输出:{'page': 1, 'limit': 10, 'timestamp': 1699999999, 'sign': 'e10adc3949ba59abbe56e057f20f883e'}
3. 常见变体与注意事项
  • 加盐方式:固定盐(硬编码)、动态盐(接口返回、时间戳截取);
  • 字符串拼接:是否包含空值参数、是否加固定前缀 / 后缀;
  • 加密结果:16 位签名需取 md5.hexdigest()[8:-8]

(二)AES 签名:对称加密的参数混淆

1. 算法特性
  • 对称加密算法(需密钥和解密算法),常见模式:ECB、CBC(需 IV 向量);
  • 填充方式:PKCS7(默认)、PKCS5;
  • 核心逻辑:参数 JSON 序列化 → AES 加密 → Base64 编码(可选);
  • 典型场景:敏感参数加密(如用户手机号、支付信息)、整体参数签名。
2. 逆向步骤与案例
步骤 1:抓包分析请求

某用户登录接口,请求体为:

json

复制代码
{
  "data": "U2FsdGVkX1+Z8sD4F4a9xQ===",
  "sign": "a7b3d8f9e2c4b1a0f3e5d7c9b2a1f0e3"
}
  • data 为 AES 加密后的用户信息(手机号 + 密码),sign 为 AES 加密后的签名。
步骤 2:定位加密函数

搜索 AESencrypt,找到核心函数:

javascript

运行

复制代码
// 依赖 CryptoJS 库
const AES_KEY = '1234567890abcdef';  // 密钥(16 位)
const AES_IV = 'abcdef1234567890';   // IV 向量(CBC 模式需 16 位)

function aesEncrypt(data) {
  const jsonStr = JSON.stringify(data);
  const key = CryptoJS.enc.Utf8.parse(AES_KEY);
  const iv = CryptoJS.enc.Utf8.parse(AES_IV);
  // AES-CBC 模式,PKCS7 填充
  const encrypted = CryptoJS.AES.encrypt(
    jsonStr,
    key,
    {
      iv: iv,
      mode: CryptoJS.mode.CBC,
      padding: CryptoJS.pad.Pkcs7
    }
  );
  // 输出 Base64 编码后的字符串
  return encrypted.toString();
}

// 生成 sign:对参数 data 进行 AES 加密
function generateAesSign(userInfo) {
  return aesEncrypt(userInfo);
}
步骤 3:Python 模拟实现

需安装依赖:pip install pycryptodome

python

运行

复制代码
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
import base64
import json

# 配置(与前端一致)
AES_KEY = b'1234567890abcdef'  # 16 位密钥
AES_IV = b'abcdef1234567890'   # 16 位 IV 向量

def aes_encrypt(data):
    # 1. JSON 序列化
    json_str = json.dumps(data, ensure_ascii=False)
    # 2. 编码(UTF-8)
    data_bytes = json_str.encode('utf-8')
    # 3. AES-CBC 加密(PKCS7 填充)
    cipher = AES.new(AES_KEY, AES.MODE_CBC, AES_IV)
    encrypted_bytes = cipher.encrypt(pad(data_bytes, AES.block_size, style='pkcs7'))
    # 4. Base64 编码
    return base64.b64encode(encrypted_bytes).decode('utf-8')

# 测试:加密用户信息生成 sign
user_info = {'phone': '13800138000', 'password': '123456'}
sign = aes_encrypt(user_info)
print(sign)
# 输出:U2FsdGVkX1+Z8sD4F4a9xQ==="(与前端加密结果一致)
3. 常见变体与注意事项
  • 密钥来源:硬编码(前端源码搜索)、接口返回(需先爬取密钥接口)、本地存储(LocalStorage、Cookie);
  • 模式差异:ECB 模式无需 IV 向量,CBC 模式必须指定 IV;
  • 编码方式:加密后可能用 Base64 或 Hex 编码,需与前端一致。

(三)RSA 签名:非对称加密的身份认证

1. 算法特性
  • 非对称加密(公钥加密、私钥解密),常见密钥长度 1024/2048 位;
  • 核心逻辑:参数拼接 → 私钥签名(前端)→ 公钥验证(后端);
  • 典型场景:登录认证、支付接口、高安全性接口(如金融、政务系统)。
2. 逆向步骤与案例
步骤 1:抓包分析请求

某支付接口,请求参数中 sign 为 RSA 签名:

plaintext

复制代码
POST /api/pay
{
  "orderId": "ORD12345678",
  "amount": 99.9,
  "timestamp": 1699999999,
  "sign": "MIICeQYJKoZIhvcNAQcCoIICajCCAnYCAQExCzAJBgUrDgMCGgUAMAsGCSqGSIb3DQEBBQUAMIGfMA0GCSqGSIb3DQEBBQUAA4GNADCBiQKBgQCq...(长字符串)"
}
步骤 2:定位签名函数

搜索 RSAsign,找到核心函数(依赖 jsrsasign 库):

javascript

运行

复制代码
// 私钥(前端硬编码,注意:实际场景中私钥可能不会直接暴露,此处为演示)
const PRIVATE_KEY = `-----BEGIN PRIVATE KEY-----
MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAMK...
-----END PRIVATE KEY-----`;

function generateRsaSign(params) {
  // 1. 按参数名升序排序
  const sortedKeys = Object.keys(params).sort();
  // 2. 拼接参数(key=value 形式)
  let signStr = '';
  sortedKeys.forEach(key => {
    signStr += `${key}=${params[key]}`;
  });
  // 3. RSA 签名(SHA256 哈希算法)
  const rsa = new KJUR.crypto.Signature({ alg: 'SHA256withRSA' });
  rsa.init(PRIVATE_KEY);
  rsa.updateString(signStr);
  // 4. 签名结果 Base64 编码
  return rsa.signString();
}
步骤 3:Python 模拟实现

需安装依赖:pip install pycryptodome

python

运行

复制代码
from Crypto.PublicKey import RSA
from Crypto.Signature import pkcs1_15
from Crypto.Hash import SHA256
import base64

# 前端硬编码的私钥(需去除换行符和首尾标识)
PRIVATE_KEY_STR = """-----BEGIN PRIVATE KEY-----
MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAMK...
-----END PRIVATE KEY-----"""

def generate_rsa_sign(params):
    # 1. 按参数名升序排序
    sorted_keys = sorted(params.keys())
    # 2. 拼接参数
    sign_str = ''.join([f"{key}={params[key]}" for key in sorted_keys])
    # 3. 加载私钥
    private_key = RSA.import_key(PRIVATE_KEY_STR)
    # 4. SHA256 哈希 + RSA 签名
    hash_obj = SHA256.new(sign_str.encode('utf-8'))
    signature = pkcs1_15.new(private_key).sign(hash_obj)
    # 5. Base64 编码
    return base64.b64encode(signature).decode('utf-8')

# 测试:生成 RSA 签名
params = {
    'orderId': 'ORD12345678',
    'amount': 99.9,
    'timestamp': 1699999999
}
sign = generate_rsa_sign(params)
print(sign)
# 输出:与前端一致的长字符串签名
3. 常见变体与注意事项
  • 签名算法:常见 SHA256withRSASHA1withRSA,需与前端一致;
  • 私钥格式:PEM 格式(含 BEGIN PRIVATE KEY 标识),需去除换行符后导入;
  • 实际场景:前端通常不会直接暴露私钥,可能通过加密传输或后端签名后返回,需结合抓包分析密钥来源。

(四)SM2 签名:国密算法的安全防护

1. 算法特性
  • 国密非对称加密算法(替代 RSA),密钥长度 256 位;
  • 核心逻辑:参数拼接 → SM3 哈希 → SM2 私钥签名;
  • 典型场景:国内政务系统、金融机构、国企接口(如社保查询、银行支付)。
2. 逆向步骤与案例
步骤 1:抓包分析请求

某政务接口,请求参数中 sign 为 SM2 签名:

plaintext

复制代码
GET /api/social-security?userId=123456&date=2024-01-01&sign=MEUCIG...(国密签名串)
步骤 2:定位签名函数

搜索 SM2sign,找到核心函数(依赖 sm-crypto 库):

javascript

运行

复制代码
// 私钥(16 进制字符串)
const SM2_PRIVATE_KEY = '00B9AB0B828FF68872F21A837FC303668428DEA11DCD1B24429D0C99E24EED83D5';

function generateSm2Sign(params) {
  // 1. 按参数名升序排序
  const sortedKeys = Object.keys(params).sort();
  // 2. 拼接参数
  let signStr = '';
  sortedKeys.forEach(key => {
    signStr += `${key}=${params[key]}&`;
  });
  signStr = signStr.slice(0, -1);  // 去除末尾 &
  // 3. SM2 签名(SM3 哈希)
  const sm2 = new SM2Utils();
  const signature = sm2.sign(signStr, SM2_PRIVATE_KEY, {
    hash: true,  // 启用 SM3 哈希
    der: true    // 输出 DER 格式
  });
  return signature;
}
步骤 3:Python 模拟实现

需安装依赖:pip install gmssl

python

运行

复制代码
from gmssl import sm2, func
import json

# 配置(与前端一致)
SM2_PRIVATE_KEY = '00B9AB0B828FF68872F21A837FC303668428DEA11DCD1B24429D0C99E24EED83D5'
SM2_PUBLIC_KEY = 'B9C9A6E04E9C91F7BA88042927374704B5EFE49391C02916169D9536967D6AFE'  # 公钥(验证用)

def generate_sm2_sign(params):
    # 1. 按参数名升序排序
    sorted_keys = sorted(params.keys())
    # 2. 拼接参数
    sign_str = '&'.join([f"{key}={params[key]}" for key in sorted_keys])
    # 3. 初始化 SM2 实例
    sm2_crypto = sm2.CryptSM2(
        public_key=SM2_PUBLIC_KEY,
        private_key=SM2_PRIVATE_KEY
    )
    # 4. SM3 哈希 + SM2 签名(DER 格式)
    sign_str_bytes = sign_str.encode('utf-8')
    signature = sm2_crypto.sign(sign_str_bytes, func.random_hex(64))  # 随机数(64 位 16 进制)
    return signature

# 测试:生成 SM2 签名
params = {
    'userId': '123456',
    'date': '2024-01-01'
}
sign = generate_sm2_sign(params)
print(sign)
# 输出:与前端一致的国密签名串
3. 常见变体与注意事项
  • 签名格式:DER 格式(默认)或 RAW 格式,需与前端一致;
  • 随机数:SM2 签名需随机数参与,前端可能使用固定随机数或动态生成,需逆向确认;
  • 库依赖:前端常用 sm-cryptogm-crypto,Python 对应 gmssl,需确保算法参数一致。

三、签名算法逆向工具集

1. 抓包工具

  • Chrome 开发者工具(Network 面板):快速抓包、筛选请求、查看参数;
  • Fiddler/Charles:支持 HTTPS 解密、请求重放、参数修改;
  • Mitmproxy:可编程抓包工具,适合批量处理和自动化测试。

2. 前端调试工具

  • Chrome 开发者工具(Sources 面板):断点调试、变量监控、代码格式化;
  • AST 反混淆工具(如 js-beautifyde4js):处理压缩、混淆的 JS 代码;
  • Hook 工具(如 TampermonkeyFrida):拦截加密函数,获取输入输出参数。

3. Python 加密库

算法 依赖库 核心功能
MD5 内置 hashlib 哈希计算
AES pycryptodome 对称加密 / 解密
RSA pycryptodome 非对称签名 / 验证
SM2 gmssl 国密签名 / 加密

四、避坑指南:常见问题与解决方案

1. 签名不一致?先查这 5 点

  • 参数排序:是否与前端一致(ASCII 升序 / 降序);
  • 字符串拼接:是否包含空值、是否有多余空格 / 符号;
  • 编码格式:是否为 UTF-8,是否包含 BOM 头;
  • 密钥 / IV:是否与前端完全一致(大小写、空格、编码);
  • 算法细节:哈希算法(SHA256/SHA1)、填充方式(PKCS7/PKCS5)、签名格式(DER/RAW)。

2. 找不到签名函数?

  • 关键词扩展:搜索 encryptdecrypthashsignsignaturetoken
  • 抓包过滤:筛选包含签名参数的请求,查看其 Initiator(发起者)定位 JS 文件;
  • 动态调试:在 Network 面板右键请求 → Copy → Copy as cURL,在终端执行,同时在 Sources 面板断点监控。

3. 密钥动态变化?

  • 抓包分析:查看是否有前置接口返回密钥(如 /api/getKey);
  • 本地存储:检查 LocalStorage、SessionStorage、Cookie 中是否存储密钥;
  • 动态生成:分析 JS 中密钥的生成逻辑(如基于设备信息、时间戳计算)。

4. 遇到反调试 / 反爬?

  • 禁用断点调试:前端可能通过 debugger 语句干扰调试,可在 Chrome 开发者工具中禁用;
  • 绕过证书验证:HTTPS 接口需配置 SSL 证书,避免因证书问题导致请求失败;
  • 模拟真实环境:设置合理的 User-Agent、Referer、Cookie,避免被识别为爬虫。

五、总结

请求参数签名算法逆向的核心是 "还原前端逻辑",无论 MD5、AES、RSA 还是 SM2,都遵循 "定位 - 解析 - 实现 - 验证" 的流程。关键在于:

  1. 熟练使用抓包和调试工具,精准定位签名生成逻辑;
  2. 细致分析参数处理规则和算法细节,不遗漏任何一个小细节;
  3. 借助 Python 加密库快速实现,通过反复调试验证结果。

随着网站反爬技术的升级,签名算法可能会结合多种加密方式(如 AES+RSA 混合加密)或动态密钥,但核心思路不变。掌握本文的逆向方法和工具,即可应对绝大多数签名验证场景。实践是提升逆向能力的关键,建议多找真实接口练习,积累不同场景的处理经验。

相关推荐
秋邱1 小时前
2025 年突破性科技:大模型驱动的实时多模态数据流处理系统
人工智能·科技·算法·机器学习
sin_hielo1 小时前
leetcode 2141
数据结构·算法·leetcode
qq_433554541 小时前
C++ 最长单调子序列
c++·算法·图论
youngee111 小时前
hot100-39二叉树层序遍历
数据结构·算法
A达峰绮1 小时前
当企业级前端遇见AI,我们如何重新定义开发效率
前端·人工智能·状态模式
未可知7771 小时前
软件设计师(下午题2)、UML与设计模式
算法·设计模式·职场和发展·uml
啊吧怪不啊吧1 小时前
贪心算法(局部最优实现全局最优)第二篇
大数据·算法·leetcode·贪心算法
杰克尼1 小时前
蓝桥云课-小蓝做题
java·数据结构·算法
MicroTech20251 小时前
微算法科技(NASDAQ:MLGO)使用区块链和迁移学习技术进行安全的IoT数据传输
科技·算法·区块链