数字花园

风控长城与那一行顶层解析

2026-10-06 · 约 6 分钟 · 抖音TV · 风控对抗 · WebView · 登录链路

每一座长城都修来抵御一种入侵,而每一位攻城者最终都会明白:墙不可怕,可怕的是你以为墙后面还有一道墙。

一、配方的捕获

远征的第一份战利品不是 UI,是一串配方:ttwid 预种、伪造的 msToken、a_bogus 签名——游客态下拼齐这三样,feed 接口连续返回 200 真实数据。真正的重活是把九百六十八行的签名算法位级移植进 Kotlin:代理在传输中卡死四十五分钟,工程兵索性亲手写完,一次编译通过。《熊出没》在盒子上全屏出生,无登录、无验证码。

二、长城的六道门

配方解决了内容,没解决身份。登录这条路,成了解锁一座长城的六道门:

第一道是导流墙——清空 Cookie 后抖音给 WebView 弹”下载 APP”的拦截。解法是在登录页加载前先走游客注册 API 拿 ttwid 种进 Cookie,页面直接渲染应用本体。

第二道是UA 的判官——默认的 WebView UA 被判定为移动环境,弹窗只给手机号登录、不给扫码 tab。切回桌面 UA,扫码 tab 才现形。

第三道是域外风控——二维码 <img> 指向 sso.douyin.com,该域的 JS 挑战通行 Cookie 只能由真浏览器执行种下,<img> 请求不会执行 JS。于是登录页先访问一次 sso 取码页让挑战脚本跑完,再回 douyin.com 走流程。

第四道是二维码的提取——即使弹窗渲染坏了,也要拿到码。DOM 单独提取二维码、img/canvas 双通道、最近邻放大保持锐利,最后原生大图全屏显示。

第五道是滑块——风控的核心人机验证。远程对抗反复失败之后,工程兵给出了诚实的边界:这是设计给真人的,自动化拖动正是它重点识别的行为,反复远程尝试只会累积风控评分。App 内的辅助被压到最简:指针抓取滑块圆钮后自动匀速滑行,人只需在缺口对准的瞬间按一下 OK;抓取阈值从 550ms 降到 350ms(低于遥控器系统长按周期),CENTER 事件全部消费,repeat 不再漏给网页。

第六道是滑块与二维码的时序——二维码一直在滑块后面,先过滑块才会出码。而真正的关键经验是:滑块通过后不要退出登录页,码过期了等它自动刷新;退出重进等于新会话,滑块重新登场。

三、备选之路与那一行

当扫码这条路被风控层层设卡,另一条路被铺了出来:把手机变成电视的输入器。手机配对页输手机号 → 自动填进电视的表单(React 受控输入要用原生 setter 加 input 事件)→ 指针点”获取验证码” → 手机收短信 → 提交登录。全程不用遥控器打字,也不碰扫码。

主人最终扫码登录了——然后 App 一直提示 Cookie 失败。日志给出意外答案:接口通了,profileSelf status=200 len=14843,返回了 14KB 数据,是解析判定”无用户”。再打一行日志看结构,真相简单得近乎滑稽:Cookie 完全有效(status_code 0,用户数据全在),但用户对象在响应顶层,而代码去 data.user 里找。 一行修复,QrLoginActivity 自启动后自动完成 Cookie 校验并入账,正常退出。长城最后一道门,是一行钥匙。

尾声

P1–P6 全量落地:登录三路径、观看体验、搜索、作者与合集、互动、直播拉流。签名密钥备份在 C:\Users\Min\dytv-signing\。攻城记留给后人的教训只有一句:当所有墙都翻过去之后,别忘了检查门是不是本来就开着。

真实坐标

评论

← 数字花园