多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

Trae MCP 服务初体验:用 Node.js 复现某视频请求头 x-ca-sign 逆向链路

Trae MCP 服务初体验:用 Node.js 复现某视频请求头 x-ca-sign 逆向链路 1. 从一次抓包说起x-ca-sign 到底是什么如果你在浏览器开发者工具里翻过某些视频类 App 的网页端请求大概率见过一个叫x-ca-sign的请求头。它不像token那样一眼能看懂也不像User-Agent那样固定不变而是每次请求都在变长度还挺规整一看就是某种签名。这个x-ca-sign请求头参数逆向本质上就是搞清楚三件事签名原文怎么拼、密钥从哪来、用什么算法算出来。我这次要复现的目标是一个视频站点在加载首页推荐流时发出的接口请求。请求路径类似/m-station/app/page带了position、pageNum、personalRecommend这几个查询参数。服务端返回的响应头里有一行access-control-allow-headers: x-ca-sign,t,ct,cv,uet这等于直接告诉我们服务端认这几个自定义头其中x-ca-sign就是签名t是时间戳。这种命名风格和阿里云 API 网关的签名机制非常接近所以逆向方向基本可以锁定为 HMAC 系列。为什么要在 Trae 里用 MCP 来做这件事因为传统逆向的流程很碎手动下载 JS、在几万行混淆代码里搜关键字、反复改脚本试参数。而 MCPModel Context Protocol服务可以把「下载 JS 文件」「正则搜索」「读写本地文件」「执行 Node 脚本」这些动作变成模型可以调用的工具。你只需要在 Trae 里描述目标它就能按步骤去抓文件、搜特征、生成脚本、跑验证。这篇教程面向的是想第一次把 MCP 服务跑起来、同时想搞懂一个真实签名链路的前端或 Node.js 开发者。哪怕你之前没接触过 MCP只要会装 Node.js、会看浏览器 Network 面板就能跟着走完。需要先说明一点本文复现的是公开网页端请求头的参数生成逻辑目的是学习签名算法与 MCP 工具链的配合方式。实际请求能否拿到完整业务数据还取决于clientVersion、token等业务字段是否有效签名正确只是第一步。下面我会把 MCP 配置、Node.js 签名脚本、抓包对照验证三块拆开讲每一步都给可复制的代码。2. Trae MCP 服务前置准备与 TaoToken 接入配置在 Trae 里用 MCP核心是两件事一是让 Trae 能调用模型来驱动工具二是把 MCP Server 注册进去。模型调用这块我用的是 TaoToken 的兼容接口它的 Base URL 是https://taotoken.net/api兼容 OpenAI 风格的请求格式在 Trae 的模型配置里填上就能用。API Key 在控制台生成地址是https://taotoken.net/api-keys生成后复制那串sk-开头的字符串。先说你本地需要装什么。Node.js 建议 18 以上因为后面脚本里用到了内置crypto和https18 版本对 ES 模块和顶层 await 支持更稳。装完后在终端跑node -v确认版本。然后建一个工作目录比如e:\projects\python_projects\js\demo这个目录就是 excerpt 里提到的js/demo所有脚本和下载的 JS 都放这里。Trae 的 MCP 配置一般写在项目根目录或用户配置目录下的 JSON 文件里。不同版本路径略有差异常见的是.trae/mcp.json或设置里的 MCP Servers 面板。下面是一份可直接复制的配置片段把文件系统类和命令执行类的 MCP Server 都注册进去{ mcpServers: { filesystem: { command: npx, args: [ -y, modelcontextprotocol/server-filesystem, e:\\projects\\python_projects\\js\\demo ] }, shell: { command: npx, args: [-y, modelcontextprotocol/server-shell] } } }这里filesystem负责读写js/demo目录下的文件shell负责执行node命令。配置里的路径要和你的实际工作目录一致Windows 下反斜杠要转义成\\。保存后重启 Trae在 MCP 面板里应该能看到这两个 Server 变成已连接状态。模型侧配置我用的是 TaoToken 的 Coding Plan适合这种需要多轮工具调用、反复跑脚本的场景。在 Trae 的模型设置里Base URL 填https://taotoken.net/apiAPI Key 填控制台生成的那串Model ID 按你订阅的填比如claude-sonnet-4-5这类。三件套齐了之后Trae 才能把「搜索 JS 里的签名函数」这种任务拆成多次工具调用去执行。注意MCP Server 的command用npx时首次运行会临时下载包网络慢的话会卡住。可以提前在终端手动跑一次npx -y modelcontextprotocol/server-filesystem --help把包缓存下来。配置完成后你可以先在 Trae 对话框里发一句「列出 js/demo 目录下的文件」如果 MCP 正常工作它会调用 filesystem 工具返回目录内容。这一步通了后面的逆向流程才有工具支撑。3. 可复制的 MCP 配置与 Node.js 签名脚本这一节是全文的核心我会把签名脚本完整写出来并解释每一行在干什么。先明确签名原文的拼接规则这是从抓包和 JS 分析里反推出来的第一行是 HTTP 方法大写比如GET第二行是请求路径不带域名和查询串比如/m-station/app/page第三行是查询参数按 key 字典序排序后用拼接比如pageNum1personalRecommend0positionCHANNEL_USK。这三行用换行符\n连起来就是待签名字符串。然后用 HMAC-SHA256 对这个字符串做摘要密钥是空字符串网页端把密钥硬编码或留空了最后把二进制摘要做 Base64 编码得到的就是x-ca-sign的值。下面是完整的 Node.js 脚本保存到js/demo/x_ca_sign_complete.jsconst crypto require(crypto); const https require(https); class RRMJXCaSignGenerator { constructor(options {}) { this.appSecret options.appSecret || ; } generateSign(params) { const { method GET, path, queryParams {}, timestamp Date.now().toString() } params; const lines [method.toUpperCase(), path]; if (queryParams Object.keys(queryParams).length 0) { const sortedParams Object.keys(queryParams) .sort() .map(key ${key}${queryParams[key]}) .join(); lines.push(sortedParams); } const stringToSign lines.join(\n); const sign crypto .createHmac(sha256, this.appSecret) .update(stringToSign) .digest(base64); return { x-ca-sign: sign, t: timestamp, _stringToSign: stringToSign }; } } function makeRequest(url, headers) { return new Promise((resolve, reject) { https.get(url, { headers }, (res) { let data ; res.on(data, chunk data chunk); res.on(end, () resolve({ status: res.statusCode, data })); }).on(error, reject).setTimeout(10000, () reject(new Error(Timeout))); }); } function decryptData(encryptedStr) { if (!encryptedStr || typeof encryptedStr ! string) return null; try { const decoded Buffer.from(encryptedStr, base64).toString(utf8); try { return JSON.parse(decoded); } catch (e) { return decoded; } } catch (e) { return null; } } async function main() { const signer new RRMJXCaSignGenerator({ appSecret: }); const config { method: GET, path: /m-station/app/page, queryParams: { position: CHANNEL_USK, pageNum: 1, personalRecommend: 0 } }; const signResult signer.generateSign(config); console.log(String to Sign:); console.log(signResult._stringToSign); console.log(x-ca-sign:, signResult[x-ca-sign]); console.log(t:, signResult.t); const queryString Object.entries(config.queryParams) .map(([k, v]) ${k}${v}) .join(); const url https://api.rrmj.plus${config.path}?${queryString}; const headers { Accept: application/json, text/plain, */*, Origin: https://m.yichengwlkj.com, Referer: https://m.yichengwlkj.com/, User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36, clientType: web_pc, clientVersion: 1.0.0, ct: web_pc, cv: 1.0.0, t: signResult.t, x-ca-sign: signResult[x-ca-sign] }; try { const response await makeRequest(url, headers); console.log(Status:, response.status); console.log(Response:, response.data.substring(0, 500)); } catch (error) { console.error(Request failed:, error.message); } } if (require.main module) { main(); } module.exports { RRMJXCaSignGenerator, makeRequest, decryptData };跑之前先确认js/demo目录存在然后执行node js/demo/x_ca_sign_complete.js你会看到控制台打印出String to Sign的三行内容、生成的x-ca-sign值和时间戳。这个脚本只依赖 Node.js 内置的crypto和https不需要npm install任何东西这也是它适合放进 MCP 工具链的原因——模型生成的脚本能直接跑不用处理依赖安装。如果你想把签名逻辑单独抽出来在别的项目里用可以只require这个文件里的RRMJXCaSignGenerator类传入appSecret和参数对象即可。注意queryParams的 key 排序是必须的服务端校验时也按同样顺序拼原文顺序错了签名就对不上。4. 验证请求与抓包对照确认签名真的对上了脚本跑通不代表签名就对了得拿浏览器抓包的结果做对照。打开目标网页按 F12 进 Network 面板筛选app/page这个请求找到 Request Headers 里的x-ca-sign和t。把这两个值复制下来和你脚本输出的做对比。注意t是毫秒时间戳每次请求都不同所以不可能完全相等但格式应该一致都是 13 位数字。真正能验证签名逻辑对不对的方法是拿浏览器里同一个请求的t值代入你的脚本重新算一遍看算出来的x-ca-sign是否和浏览器里的一致。具体做法在脚本里把timestamp手动设成浏览器抓到的那个tqueryParams也照抄浏览器请求 URL 里的参数然后跑脚本。如果输出的x-ca-sign和浏览器里的完全相同说明你的签名原文拼接规则和算法都对了。我实测下来第一次对照时签名对不上排查发现是查询参数里多了一个浏览器自动加的_t缓存参数而服务端签名时并不包含它。把_t从queryParams里去掉后签名立刻一致。这个坑很典型浏览器请求 URL 里的参数不一定全部参与签名要以服务端实际校验的为准。判断方法就是看响应头里access-control-allow-headers列出的字段以及多试几次对照。验证通过后你可以把脚本里的请求部分打开实际发一次请求。如果返回的code是0000且data字段是一串 Base64 字符串说明签名被服务端接受了。这串 Base64 解码后通常是 JSON里面是视频列表数据。如果返回「客户端版本过低」之类的提示那是clientVersion或token的业务校验没过和签名无关签名这一环已经成功了。为了让你更直观地对照下面这张表列出关键字段的作用字段来源是否参与签名说明x-ca-signHMAC-SHA256 计算是结果签名值Base64 编码tDate.now()是作为时间戳行毫秒级时间戳position业务参数是频道标识pageNum业务参数是页码personalRecommend业务参数是是否个性化推荐_t浏览器缓存参数否不参与签名需剔除token登录态否业务鉴权不参与签名把这张表存下来下次遇到类似接口先按「哪些参数进签名原文」这个维度去分类能省很多试错时间。5. 常见报错排查401、local proxy failed 与签名不匹配跑这套流程时最容易卡在几个固定报错上。我按出现频率从高到低排一下每个都给排查路径。第一个是401 Unauthorized。这个报错通常不是签名算错而是请求头里缺了服务端要求的鉴权字段。回到响应头看access-control-allow-headers它列了x-ca-sign,t,ct,cv,uet说明这几个头都得带上。ct和cv是客户端类型和版本uet是用户环境标识。少任何一个服务端都可能直接 401。排查方法把浏览器请求的完整 Request Headers 复制出来逐个对照你的脚本缺哪个补哪个。第二个是local proxy failed或连接超时。这个多半是 MCP 的 shell 工具执行node命令时工作目录不对或者npx拉包卡住了。先在终端手动cd到js/demo再跑脚本确认脚本本身能跑通。如果手动能跑、MCP 里跑不了检查 MCP 配置里 filesystem 的路径参数是不是指向了正确目录。另外https.get请求外部接口时如果本地网络对api.rrmj.plus解析有问题也会超时可以先用curl或浏览器直接访问该接口确认连通性。第三个是签名不匹配服务端返回类似「签名错误」的提示。这个要分两步查先确认String to Sign的三行拼接是否符合「方法\n路径\n排序后的查询串」这个格式特别注意路径不带域名、查询串按 key 字典序排。再确认密钥是不是空字符串有些接口的appSecret藏在 JS 里需要从混淆代码里搜出来。如果前两步都对那就是参与签名的参数集合不对用第 4 节的对照法拿浏览器的t值反推。第四个是reading choices这类报错。这个一般出现在模型调用侧不是签名脚本的问题。如果你在 Trae 里配置 TaoToken 的模型时Base URL 或 Model ID 填错模型返回的结构里没有choices字段工具调用就会中断。检查https://taotoken.net/api是否填在 Base URL 位置API Key 是否以sk-开头且没过期。如果用的是 Coding Plan确认订阅状态正常。第五个是 OAuth 相关的报错。有些 MCP Server 需要 OAuth 授权才能访问外部资源如果你用的是带鉴权的 Server第一次连接会弹授权页。如果授权失败检查回调地址是否和配置一致。不过本文用的 filesystem 和 shell 两个 Server 都不需要 OAuth所以这个报错一般不会遇到列出来是让你有个印象。排查时有个通用技巧把String to Sign打印出来和浏览器抓包时用同样参数算出的原文做逐字符对比。换行符、空格、参数顺序任何一个字符不同都会导致签名不同。我踩过的坑里有一次是查询串里某个参数值带了 URL 编码浏览器发出去的是编码后的值而签名原文用的是编码前的值这种细节只能靠逐字符对照发现。6. 把签名能力接进你的工作流签名脚本跑通之后它的价值不只是复现一个请求头。你可以把它封装成一个 MCP 工具让 Trae 在需要时自动调用。具体做法是在 MCP Server 里注册一个generate_x_ca_sign工具输入是 method、path、queryParams输出是签名结果。这样以后遇到同类接口直接让模型调用这个工具生成签名不用每次重写脚本。如果你经常做这类接口分析建议把签名逻辑和请求逻辑分开签名类只负责算x-ca-sign请求类负责拼 URL、发请求、解响应。这样签名类可以单独做单元测试用固定的t值和参数验证输出是否稳定。我实测下来把t固定成某个值后签名结果是确定的这给回归测试提供了基准。对于需要长期跑这类任务的场景TaoToken 的 Coding Plan 比较合适因为工具调用轮次多、上下文长按量计费的模式更划算。模型对话入口在https://taotoken.net/chat接入文档在https://taotoken.net/docAPI Key 管理在https://taotoken.net/api-keys。Claude Code 用户如果想把这类脚本生成能力接进终端工作流可以参考https://taotoken.net/claude-code的配置说明。最后留一个实用技巧把每次成功对照的String to Sign和签名结果存成一个 JSON 文件作为测试用例。下次改脚本时先跑这些用例全过了再发真实请求。这样能避免「改了一处、别处又错」的反复调试。签名逆向这件事本质是把黑盒的拼接规则变成白盒的可验证逻辑一旦用例覆盖够了后面就是体力活。
返回列表