简介最新巨量算数X-Bogus、-signature、msToken参数加密分析结果聚焦巨量引擎接口的签名与令牌机制面向爬虫开发、业务风控及安全研究人员解决请求参数逆向与自动化生成问题覆盖X-Bogus、_signature和msToken三类参数。资源共2个文件一个JS文件包含加密混淆算法用于还原上述三个参数的生成过程一个Python脚本负责调用该逻辑直接产出可用请求参数。整体仅78KB轻量易用无需安装额外依赖即可运行已有5236人学习下载。通过阅读JS混淆源码可理解参数拼接、哈希签名与令牌刷新细节配合Python脚本能快速搭建自己的参数生成模块大幅减少逆向与调试时间适用于巨量算数相关数据接口的签名研究、参数自动化构造、请求模拟及安全测试场景。适合具备一定JS逆向基础、希望深入掌握该平台加密机制的开发者。1. 巨量算数参数加密浏览器里的三道“数字围栏”打开巨量算数的实时榜单页面F12 的网络面板里随便点一条请求就能看到 URL 上并排挂着 X-Bogus、-signature 和 msToken 三个参数。把其中任何一个去掉接口都会返回异常三个都保留但生成顺序不对也会被判定为无效签名。这组参数不是简单的登录态 token而是前端 JS 在请求发出前实时计算出来的签名体系三者职责各不相同又互相绑定。1.0.0.22 是这套脚本的版本标识。不同版本里盐值、拼接顺序和 msToken 的生成位置会有细微差异。做数据采集、前端安全分析或自动化测试的人遇到最多的问题不是算法本身有多难而是参数一会儿失效、一会儿报 invalid signature detected。这里会从三个参数的职责入手把抓包定位、算法识别和本地复现的完整思路串一遍最后落到一个能直接拿去排查的检查流程。2. 三个参数的职责分工与生成时机在往下追算法之前先得把三个参数各自管什么讲清楚。它们虽然都出现在 URL 上但生成时机、变化频率和失效表现有明确差异。我把同一份榜单刷新了十次每条请求抓下来对比分类如下表。参数名出现位置变化频率典型失败表现msTokenquery 或 Header每次刷新/会话过期后变化数据为空或返回 retcode 异常X-Bogusquery 尾部每次请求都变化与 URL 严格一一对应invalid signature detected-signaturequery 末尾与业务参数、时间戳绑定403 或 missing signature需要特别注意的是X-Bogus 并不是一个简单散列值它的字符集里有大小写字母和数字混排长度也不是标准的 32 位 hex。这意味着它在生成前可能经历过字符替换和自定义编码。真正调试时先看参数落在请求的哪个位置能减少很多无用功。2.1 msToken与会话绑定的临时令牌msToken 在巨量算数里偏“会话侧”不承担具体签名任务。服务端通过它判断浏览器环境是否有自动化特征也会用它标记页面初始化状态。TikTok 的页面里也能看到同名参数但生成位置略有不同。它的生成有两条路径一种是在页面加载时由专门接口下发另一种是前端用独立 JS 生成后写入 cookie。从 1.0.0.22 的表现看它更像后者因为静态资源加载完成后页面才会第一次把它拼到请求 URL 上。想把 msToken 的生成源找出来可以先在浏览器 Console 里执行下面这段代码它会从资源列表里挑出刚才那条带 msToken 的请求// 用 Performance API 找到最近一次带 msToken 的资源地址 const resource performance.getEntriesByType(resource) .map(r r.name) .find(name name.includes(msToken)); if (resource) { const params new URL(resource).searchParams; console.log(msToken , params.get(msToken)); } else { console.log(当前页面没有暴露 msToken 的资源); }这段代码的意义是把“msToken 来自哪个请求”这个问题变成可检查的。执行结果里如果显示了 URL 路径就直接去该路径的 Initiator 栈里找赋值点如果打印出 not found说明 msToken 是写在 Header 里需要改用 Network 面板的请求头过滤。提示msToken 有效期通常只有几个小时不适合写死在本地代码里最好每次都从 cookie 或页面接口动态读取。2.2 X-Bogus请求级签名防止 URL 被篡改X-Bogus 这个命名最早在抖音 Web 端出现巨量算数沿用后行为基本一致把请求 path、query 参数键值顺序、User-Agent 和一段内置盐值一起做混淆计算输出一个长度固定但肉眼看不出规律的字符串。它对 URL 里的任何改动都敏感包括参数顺序变化和大小写变化。要判断你抓到的 X-Bogus 是 hex 还是自定义编码可以用 Python 快速看一下字符集合# 统计 X-Bogus 的字符分布判断编码方式 from collections import Counter sample Df7sZX9qR4... # 替换成你从浏览器里抓到的值 chars Counter(sample) print(长度:, len(sample)) print(字符集:, sorted(chars.keys())) # 如果长度不是 32/40/64 且包含 _说明在 base64 的基础上做过字符替换这里的关键是“字符集”不是“长度”。很多调试者一看到 32 位就按 MD5 去撞结果必然失败。X-Bogus 内部会包含一个时间戳分量和一段随机盐即使 URL 完全相同两次请求的 X-Bogus 也会不同所以不能用静态比对 URL 和签名的办法去验证只能通过控制变量法观察算法特征。2.3 -signature业务参数签名与 X-Bogus 互相校验-signature这个名字很容易让人在 URL 解析时翻车因为参数名以减号开头。直接用url.split()再取-signature是没问题的但在 JS 里用params.get(-signature)更安全。它在巨量算数中负责绑定具体的查询条件比如日期范围、维度指标、分页页码防止这些业务数据被批量替换。下面这段代码展示了如何正确读取-signature// 正确读取名为 -signature 的 query 参数 const u new URL(https://xxx/report?date20250601metricvv-signatureabc123); console.log(u.searchParams.get(-signature)); // abc123从生成顺序看三个参数有明确的先后关系msToken 先存在X-Bogus 在发送前生成最后-signature把当次业务参数和签名关联起来。服务端校验时优先看 X-Bogus 是否与 URL 一致再看-signature是否与业务参数一致。两把锁相互交叉任何一把对不上都会返回签名错误。3. 抓包与动态定位找到参数生成的那行代码参数分析不能只停留在肉眼观察。下一步要找到签名参数到底由哪一行 JS 生成。先做静态搜索再做动态 hook最后把算法抽到本地执行这条路线适用于大多数字节系页面。3.1 先从网络面板和 HAR 里筛目标请求打开巨量算数榜单页按 F12 进入 Network勾选 Fetch/XHR 后刷新页面。在过滤框输入 X-Bogus能直观看到一批带签名的请求。按时间排序后对比会发现 msToken 和 X-Bogus 总是成对出现-signature则出现在需要业务参数的接口里。这是判断“哪些接口触发完整性校验”最快的方法。如果需要批量分析多组请求先把 HAR 导出再用命令行提取参数# 从 HAR 中提取三类参数并按出现次数排序 cat export.har | jq -r .log.entries[].request.url \ | grep -oE [?](X-Bogus|-signature|msToken)[^]* \ | sort | uniq -c | sort -nr | head -30这条命令的用途是快速统计当前页面中哪些参数出现频率最高。如果某个参数只出现在个别接口说明它不与全局绑定如果所有接口都有 X-Bogus那它才值得花时间还原。3.2 Hook XHR 和 fetch捕获发送瞬间点击页面筛选按钮时签名参数已经嵌入 URL。因此 hook 发送函数是最好的捕获点。下面这段代码覆盖了 XMLHttpRequest 的 open 和 send 两个方法在 send 执行前打印完整参数// 拦截 XMLHttpRequest在 send 前输出 URL 和 body const rawOpen XMLHttpRequest.prototype.open; const rawSend XMLHttpRequest.prototype.send; XMLHttpRequest.prototype.open function(method, url) { this._sigUrl url; return rawOpen.call(this, method, url); }; XMLHttpRequest.prototype.send function(body) { const u new URL(this._sigUrl); const params u.searchParams; console.log({ path: u.pathname, msToken: params.get(msToken), XBogus: params.get(X-Bogus), signature: params.get(-signature), body: body }); return rawSend.call(this, body); };这段代码要注意两点。第一必须在新请求发生前注入优先使用 DevTools 的 Snippets 保存每次刷新后重新执行第二如果签名参数是由 XHR 的 setRequestHeader 写入而不是 URL query这段代码不会打印 header。这时需要再 hooksetRequestHeader或在 send 里读取this.getResponseHeader的镜像。3.3 用 Node.js 的 vm 执行抽取到的加密函数定位到生成函数后通常的做法是把它和依赖函数一起复制出来放到 Node.js 的 vm 沙箱里跑。下面这段脚本假设入口已经确认是 generateSignature(path, query)const vm require(vm); const fs require(fs); // 把从浏览器复制的函数源码保存为 gen.js const code fs.readFileSync(./gen.js, utf8); // 构造代码运行所需的最小全局环境 const sandbox { window: {}, document: { cookie: }, navigator: { userAgent: Mozilla/5.0 }, location: { href: https://xxx }, console }; vm.createContext(sandbox); // 执行源码并调用入口函数 vm.runInContext(code, sandbox); const output vm.runInContext( generateSignature(/v1/report/list, page1size20), sandbox ); console.log(output);vm 沙箱最关键的是补全全局对象。遇到ReferenceError: window is not defined就把 window 加进 sandbox遇到navigator is not defined就补一个带 userAgent 的对象。函数内部如果引用了动态变化的全局变量比如performance还需要额外传入performance: { timeOrigin: Date.now(), now: () 0 }这类桩对象。这个步骤虽然繁琐但能帮助你确认独立执行时输入输出与浏览器里是否一致。4. 1.0.0.22 版本下的算法还原从哈希到国密当函数已经从页面中抽离下一步是还原它的计算过程。1.0.0.22 版本的实现里有几个特征值得注意首先是参数序列化的顺序其次是摘要算法是否属于国密体系。4.1 先验证哈希还是签名输入变化与输出变化不要急着拆字节先用控制变量法确认算法的敏感度。取同一个 URL只改变 query 参数的顺序观察 X-Bogus 是否变化再改动单个字符值看输出变化是否集中。根据结果可以初步圈定算法类型输入变化输出变化算法特征参数顺序互换不变签名前先做了排序参数顺序互换变化直接拼接原始 query改一个字符输出局部变化分组加密/流式处理改一个字符输出整体变化散列或 CBC 模式这个表的价值在于排除法。比如你发现“参数顺序互换时输出不变”那就要先实现规范化排序再去想摘要逻辑。绝大多数无效签名都是因为这一步漏了。4.2 在 JS 中识别 SM1、SM2、SM3 特征字节系的前端安全代码里经常出现国密算法。SM1 是硬件对称算法密钥长度 128 位参数不公开JS 里几乎不会实现SM2 是非对称签名/加密/密钥交换密钥长度 256 位SM3 是 256 位杂凑算法和自定义置换组合后能形成很难直接从 hash 反推的签名。在 1.0.0.22 的脚本里搜索这些关键词能快速判断需要准备哪些依赖。# 静态扫描 JS 文件中国密算法的特征字符串与常量 import re from pathlib import Path js_text Path(main.1.0.0.22.js).read_text(utf-8, errorsignore) patterns [ 0x7380166f, # SM3 初始向量特征常量 SM2, SM3, SM4, ECC, prime256v1 ] for p in patterns: if re.search(p, js_text): print(found:, p)找到“0x7380166f”这个常量基本就可以确认脚本中引用了 SM3 算法。不过要注意引用算法和实际执行算法不是一回事有些脚本会把国密函数包在数组里运行时用字符串索引动态调用静态搜索只能缩短范围不能作为最终结论。4.3 本地复现的最小签名管线从工程角度我一般会把签名过程拆成四步参数清洗、排序拼接、摘要计算、字符映射。下面这段 Python 代码展示了前三步的框架替换掉最后的映射函数就能接进自己的工程import time from urllib.parse import urlencode def serialize_params(items: list[tuple[str, str]]) - str: # 去掉键值前后的空格并按 key 的字节排序 cleaned [(k.strip(), str(v).strip()) for k, v in items] cleaned.sort(keylambda kv: kv[0]) return urlencode(cleaned) def make_sign_input(path: str, query_items: list[tuple[str, str]]) - bytes: # 常见顺序path query 10 位秒级时间戳 msToken timestamp str(int(time.time())) query serialize_params(query_items) raw f{path}?{query}{timestamp} return raw.encode(utf-8) # 之后把 make_sign_input 的结果送入 SM3/hashlib再加一层字符映射 # result map_character_set(sm3_hash(make_sign_input(...)))这段代码有三个参数需要根据实际抓包调整path 是否包含前导斜杠、时间戳是秒还是毫秒、msToken 放在拼接前还是拼接后。可以先输出中间字符串与页面里同一个请求做对比逐段排查差异。字符映射这一步如果没有实际样本很难凭空猜出建议用多组 URL 和对应签名做输入输出回归反推映射表。5. invalid signature detected 的排查与参数轮换签名生成后真正花时间的是排错。下面列出我在这类接口上最常用的处理顺序。5.1 先看时间戳与签名顺序invalid signature detected 出现时第一个要排除的是时间偏差。计算机本地时间如果和 NTP 偏差超过 30 秒服务端验签就会失败。不能直接用本地时间戳要从页面运行时读取可信时间// 读取页面性能时间线避免本地时间偏差 const trustedTime Math.floor((performance.timeOrigin performance.now()) / 1000); console.log(trustedTime);如果把这个时间戳代入签名后依然报错再检查签名拼接顺序。顺序错误的表现是同一组参数在不同请求里忽好忽坏因为时间戳一变整体字符串就变某一次可能恰好撞对。5.2 用“半自动”方式解决 msToken 刷新msToken 的过期周期通常按小时算手动复制很快会失效。我一般会在本地起一个轮询脚本定期从页面接口拉取新 token 到文件签名脚本启动时再读取。这样避免反复打开浏览器复制import time import requests def fetch_token() - str: # 访问页面种子接口返回 msToken url https://xxx/your/seed/page r requests.get(url, headers{User-Agent: Mozilla/5.0}) # 优先从 cookie 取其次是 HTML 中的脚本变量 return r.cookies.get(msToken) or while True: token fetch_token() if token: with open(mstoken.txt, w) as f: f.write(token) print(updated, token[:8]) time.sleep(60)这个脚本不处理 X-Bogus所以 token 是否能刷新成功取决于种子接口是否也被签名保护。如果页面要求先签名才返回 token就需要把第 3 章的 vm 脚本包成 HTTP 服务让 token 脚本通过本地接口调用签名函数。最后确认整个链路是否通用 curl 直接打一次目标接口并在返回里看 retcodecurl -sG https://xxx/report \ --data-urlencode page1 \ --data-urlencode size20 \ --data-urlencode -signatureabc \ -H X-Bogus: xxxx \ -H msToken: xxxx | jq {retcode, message} # 若 retcode 非 0回到 5.1 检查时间戳若仍报 invalid signature detected继续排查字符映射表本文还有配套的精品资源点击获取