为什么签名需要被验证
平台对签名是抽样校验的,「请求成功」证明不了任何事;四个平台的验证器与它们各自的验证强度
为什么签名需要被验证
抖音对签名是抽样校验的:签名用错了常量,请求大部分时候照样成功,只是被判定为高风险的概率上升。它不报错、不返回空、不给你任何可分辨的信封 —— 表现为「偶尔不稳」,而不是「坏了」。所以把「接口能通」当作签名正确的证据,是无效推理。
麻烦在于,签名算法里藏着若干个常量(抖音的盐值、page_id、SDK 版本块;快手的布局魔数;小红书的模板版本……)。它们随平台改自己的前端代码而变,而变的时候没有任何通知。
自证模型的三个空转
在这套约束下,常见的测试写法一个都发现不了常量过期:
| 写法 | 为什么无效 |
|---|---|
| 自生成快照 | 首次运行时由被测代码自己写入。常量改了,快照跟着改,测试永远绿 |
| 与被测对象比自身 | expect(v7).toBe(v6) —— 但两个符号 import 的是同一个模块,等于把函数和自己比 |
| 「输出长度 > 100」这类形状断言 | 错的常量产出的签名与对的形状完全一致,只是字节不同 |
一句话:没有任何自证型断言能证伪一个常量。 这不是写得不够好,是模型的问题 —— 缺少一个不随被测代码变化的外部锚点。amagi 的数据接口就带着一个 2024 年的旧盐值活了两年(故事在抖音那页)。
四个平台,各能验到什么程度
唯一能证伪一个常量的办法,是拿平台自己的输出拆开看。每个平台的验证器都在自己那页的页顶,跑的就是 @ikenxuan/amagi/signing 本身 —— 与 oracle 测试、与线上签名同一份代码。但各平台的可验证面不一样宽,这是诚实而不是缺口:
| 平台 | 可验证的签名 | 能验到什么 | 验证强度 |
|---|---|---|---|
| 抖音 | a_bogus | 结构指纹、盐值判定、query / UA 摘要链(24 位/条)、可还原时钟与五十个标量 | ●●● 整段可拆,有真实捕获做 oracle |
| 抖音 | X-Bogus | 常量指纹、可还原时钟、16 位摘要链 | ●●○ 可拆;暂无浏览器捕获 |
| 抖音 | x-secsdk-web-signature | 确定性 MD5,重算逐字符比对 | ●●● 完全可验 |
| 抖音 | verify_fp / msToken / ttwid | 骨架与时钟;msToken / ttwid 底下没有明文 | ●○○ 部分是「说了就是答案」 |
| B站 | w_rid(wbi) | 同一组 keys、同一个 wts 重算,逐字符比对 | ●●● 完全可验 |
| B站 | av ↔ bv | 双向互转,往返自洽 | ●●● 完全可验 |
| 快手 | $HE_ 段 | 布局常量、双层 LRC、可还原时钟与计数器 | ●●○ 可拆;暂无浏览器捕获 |
| 小红书 | x-s 等头部 | 前缀、长度、字符集(XYW 信封可拆一层 JSON) | ●○○ 算法本体在 Node 侧,进不了浏览器 |
「验证强度」低一档的都不藏:快手那页解释了 HUDR 段为什么拆不开,小红书那页解释了算法本体为什么进不了浏览器。
「解码」诚实地指三件事
把这三件事混为一谈,是这类工具开始撒谎的方式:
- 可还原 —— 值就在载荷里,能原样取回:时钟、
aid、计数器。没有任何推断。 - 被绑定但不可还原 —— 载荷里带着某个输入的摘要。输入无法从摘要倒推,工具从不假装能;它做的是校验:给候选值,回答是不是签名封住的那一个,并标注证据位数。
- 根本没被计算 ——
msToken、ttwid、HUDR 密文由平台签发或整体加密,底下没有明文可找。说出这一点就是答案,而不是「没能给出答案」。
下一步
抖音
a_bogus / X-Bogus / secsdk / 访客令牌 —— 四个验证器,以及各自的常量表与边界
B站
w_rid 重算比对与 av/bv 互转 —— 两个完全可验的纯函数签名
快手
$HE_ 段可整段拆开(时钟与计数器原样读回);HUDR 段拆不开,说了就是答案
小红书
x-s 的算法本体进不了浏览器 —— 这里只有形状检查;XYW 信封能拆一层 JSON
最后更新于 2026-10-02