快手
$HE_ 段可整段拆开(时钟与计数器原样读回);HUDR 段拆不开,说了就是答案
快手签名验证
快手的 __NS_hxfalcon 签名由两段拼成:HUDR_<base64url 密文> + $HE_<hex>。两段的可验证面完全不同 —— $HE_ 段布局固定、可整段拆开;HUDR 段整段是 ChaCha 密文,底下没有明文可找。
布局常量 + 双层 LRC + 可还原时钟与计数器
签名输入
判定
这是一份结构良好的 $HE_ 段 —— 两层 LRC 与全部布局常量都对上。
$HE_ 段:能拆到什么
最终输出是 45 字节 hex,最后一个字节是异或键 —— 它本身是前 44 字节的 LRC 校验,同时把那 44 字节逐个异或成密文。所以解码顺序固定为「拆键 → 还原 → 逐段校验 → 读字段」:
| 字段 | 位置(还原后) | 能验 / 能读 |
|---|---|---|
| header 魔数 | 0–1 | 恒为 4B54 |
| 版本块 | 2–3 | 恒为 cda9 |
| 启动标记 | 4 | 恒为 ab |
| startupRandom | 5–10 | 原样读回(页面加载时的一次性随机数) |
| random | 11–16 | 原样读回(本条签名自己的 48 位随机数) |
| 固定体 | 17–21 | 恒为 0100000001 |
| count | 22–25 | 异或掩码已解掉,原样读回 |
| hash field | 26–29 | 读得出;重算需要 signInput + HUDR body |
| 时钟 | 30–35 | 异或掩码已解掉,原样读回(毫秒) |
| 尾段 | 36–42 | 恒为 9b563eda7b563e |
| 两层 LRC | 43 / 44 | 信封 LRC 覆盖全部 44 字节,尾段 LRC 只覆盖尾段 |
能同时通过两层校验与四个布局常量的值,必然是这套装配顺序产出的 —— 这就是「结构指纹」,与 a_bogus 的校验和同理。
hash field 为什么不自动重算
它绑的是 signInput + HUDR body:前者由请求 URL 与载荷推导,后者是随实例状态滚动的
密文。捕获签名时这两样通常不在手边 —— 输入凑不齐就别猜,凑齐了可以用
deriveKuaishouHeHashFieldHex 自行比对。
HUDR 段:拆不开,说了就是答案
HUDR 段的载荷经过掩码与 ChaCha 加密(密钥写死在前端代码里,amagi 与之对齐),内容对验证器而言是不可还原的。能验的只有外壳:HUDR_ 前缀与 base64url 字符集。这不是缺口 —— 它的作用就是让外部无法拼装签名,能拆开反而是坏事。
诚实的局限:没有外部 oracle
$HE_ 的结构断言全部来自本仓库实现与前端代码的对齐,仓库里没有一份浏览器产出的快手签名捕获(不像抖音的 a_bogus 有 oracle fixture)。所以「结构良好」证明的是装配顺序对齐,不能证明常量全部新鲜。快手前端若改了布局常量(header、版本块、尾段),验证器会开始报「对不上」—— 那时该重新对一遍前端代码。
签名状态随实例走
count 随每次签名递增、startupRandom 按实例生成、匿名 kww 按实例缓存 ——
同进程两个 client 的签名互不干扰。对比两份签名时,别把 count 或随机数的不同
当成「有一个是错的」。
下一步
为什么签名需要被验证
平台对签名是抽样校验的,「请求成功」证明不了任何事;四个平台的验证器与它们各自的验证强度
抖音
a_bogus / X-Bogus / secsdk / 访客令牌 —— 四个验证器,以及各自的常量表与边界
B站
w_rid 重算比对与 av/bv 互转 —— 两个完全可验的纯函数签名
小红书
x-s 的算法本体进不了浏览器 —— 这里只有形状检查;XYW 信封能拆一层 JSON
最后更新于 2026-10-02