校验更新了,导致上次补的签名失效,返回空。继续让AI分析。最终消耗了Trae 1400积分。
问题概述
TikTok 网页版 /api/post/item_list/ 接口(用户作品列表)请求返回空数据,原因是补环境生成的签名无效。
根本原因分析
1. envcode 被非法修改
现象:在签名生成过程中,860 状态表的 envcode 字段被 webmssdk 内部逻辑从 1 改为 4105。
根因:补环境缺少完整的 WebGL 上下文,导致 webmssdk 无法正确获取 GPU 信息,进而将环境编码计算为 4105(非浏览器标记),使服务端校验失败。
影响:签名中的环境指纹与浏览器不一致,服务端拒绝请求返回空数据。
2. WebGL 上下文缺失
现象:原始补环境中 HTMLCanvasElement.prototype.getContext 返回 null。
根因:TikTok 的 webmssdk 在初始化和签名过程中会调用 Canvas 的 getContext('webgl') 来获取 GPU 指纹信息,包括:
UNMASKED_VENDOR_WEBGL(0x9245)UNMASKED_RENDERER_WEBGL(0x9246)- WebGL 扩展列表
- 最大纹理尺寸等参数
影响:envcode 计算错误,签名无效。
3. msToken 时效性
现象:即使签名正确,msToken 过期后请求也会返回空数据。
根因:msToken 是会话标识符,具有时效性,过期后必须从浏览器获取最新值。
解决方案
修复 1:完整的 WebGL 模拟
在 get_sign.js 中实现完整的 WebGL 上下文对象:
// 核心实现:createMockWebGLContext()
// 提供完整的 getParameter 实现
// 支持所有 WebGL 常量查询
// 包括 WEBGL_debug_renderer_info 扩展
关键参数:
const _webGLInfo = {
vendor: 'WebKit',
renderer: 'WebKit WebGL',
unmaskedVendor: 'Google Inc. (NVIDIA)',
unmaskedRenderer: 'ANGLE (NVIDIA, NVIDIA GeForce RTX 3060 ...)',
maxTextureSize: 16384,
maxCubeMapTextureSize: 16384,
maxRenderbufferSize: 16384,
maxTextureImageUnits: 16,
maxVertexTextureImageUnits: 16,
maxCombinedTextureImageUnits: 32,
maxVertexAttribs: 16,
maxVertexUniformVectors: 4095,
maxFragmentUniformVectors: 1024,
maxVaryingVectors: 30,
extensions: [/* 完整的 WebGL 扩展列表 */]
};
修复 2:预初始化状态注入
在 ba.init() 之前注入关键状态表:
// 854: WebGL 状态(防止 webmssdk 重新获取 GPU 信息)
// 868: Canvas hash
// 从 browser_state.json 读取并注入
const bs = JSON.parse(fs.readFileSync('browser_state.json'));
sandbox.__msdkTable[854].v = bs['854'];
sandbox.__msdkTable[868].v = bs['868'];
修复 3:Proxy 保护 envcode
使用 JavaScript Proxy 拦截对 envcode 的修改:
const proxy860 = new Proxy(originalV, {
set(target, prop, value) {
if (prop === 'envcode' && value !== 1) {
console.error('[proxy-block] envcode blocked');
return true; // 阻止修改
}
target[prop] = value;
return true;
}
});
sandbox.__msdkTable['860'].v = proxy860;
sandbox.__msdkTable['938'].v = proxy938; // 938 与 860 同步
涉及的状态表(环境参数)
| 状态表 ID | 名称 | 作用 | 数据来源 |
|---|---|---|---|
| 854 | WebGL 状态 | GPU 指纹信息 | browser_state.json |
| 860 | msStatus | 签名状态核心(含 envcode、exBundleProof) | browser_state.json |
| 868 | Canvas hash | Canvas 指纹哈希 | browser_state.json |
| 917 | 设备指纹 | 浏览器环境指纹数据 | fp_data.json |
| 921 | 浏览器信息 | 电池、权限、WebGL 等信息 | browser_state.json |
| 938 | msStatus(副本) | 与 860 同步 | 自动同步 |
| 980 | 鼠标轨迹 | 鼠标移动轨迹数据 | browser_state.json |
关键请求参数
| 参数 | 说明 | 示例值 |
|---|---|---|
aid |
应用 ID | 1988 |
count |
返回数量 | 16 |
cursor |
分页游标 | 0 或上次返回的 cursor |
secUid |
安全用户 ID | MS4wLjABAAAA... |
clientABVersions |
实验性功能版本 | 35个版本ID用逗号分隔 |
enable_cache |
是否启用缓存 | false |
post_item_list_request_type |
请求类型 | 0 |
needPinnedItemIds |
是否需要置顶视频 | true |
video_encoding |
视频编码 | dash |
coverFormat |
封面格式 | 2 |
签名参数说明
| 参数 | 说明 | 来源 |
|---|---|---|
X-Dynosaur |
主签名字段,包含环境指纹 | webmssdk 生成 |
X-Bogus |
辅助签名,通常为 1 |
webmssdk 生成 |
X-Gnarly |
附加签名 | webmssdk 生成 |
msToken |
非必须,从浏览器获取 | Cookie |
使用方法
1. 获取最新 msToken
// 在浏览器控制台执行
document.cookie.split(';')
.find(c => c.trim().startsWith('msToken='))
.split('=')[1]
或使用 MCP 工具:
# js-reverse MCP
evaluate_script("() => document.cookie.match(/msToken=([^;]+)/)?.[1]")
2. 运行补环境生成签名
node get_sign.js "<URL>" "<UA>" "<msToken>"
参数说明:
- URL: 完整的 API 请求 URL(包含所有查询参数)
- UA: 浏览器 User-Agent
- msToken: 从浏览器获取的最新 msToken
示例:
node get_sign.js \
"https://www.tiktok.com/api/post/item_list/?aid=1988&..." \
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) ..." \
"BbXpLlis3IVHHQGv3cPPQISfdmfa8HyrUgWL8..."
3. 使用生成的签名 URL 请求接口
import requests
signed_url = "get_sign.js 输出的 URL"
headers = {
'User-Agent': 'Mozilla/5.0 ...',
'Referer': 'https://www.tiktok.com/@ainbag9',
'Cookie': 'msToken=<最新msToken>'
}
response = requests.get(signed_url, headers=headers)
data = response.json()
items = data.get('itemList', [])
核心文件说明
| 文件 | 作用 | 说明 |
|---|---|---|
get_sign.js |
核心签名脚本 | 补环境实现 + 签名生成 |
webmssdk.js |
签名 SDK | TikTok 的签名算法实现 |
browser_state.json |
浏览器状态 | 从浏览器抓取的状态表数据 |
fp_data.json |
设备指纹 | 从浏览器抓取的指纹数据 |
test_with_new_ms.py |
测试脚本 | 示例测试脚本 |
调试技巧
环境变量
# 启用签名日志
SIG_LOG=1 node get_sign.js ...
# Dump 状态表到文件
DUMP_TABLE=1 node get_sign.js ...
# 记录键访问
TRACE_KEYS=1 node get_sign.js ...
日志标记
[pre-sign]: 签名前状态[post-sign]: 签名后状态[proxy-block]: Proxy 拦截日志[inject]: 状态注入日志
注意事项
- msToken 必须新鲜:过期后签名正确也会返回空数据
- 环境指纹必须匹配:860、917、921 等状态表必须与浏览器一致
- WebGL 模拟必须完整:缺少任何关键参数都可能导致 envcode 计算错误
- clientABVersions 参数:TikTok 新版接口需要此参数,从浏览器抓取最新值
- IP 风控:同一 IP 频繁请求可能被风控,建议使用代理
验证结果
✅ 成功获取 14 个视频
✅ 签名与浏览器一致
✅ envcode 保持为正确值 1
✅ 支持 post/item_list 接口