公众号旧文
JS challenge 到底在 challenge 什么?
之前胖子在使用 yt-dlp 时,看到过类似这样的提示:

“JS challenge failed”
“无法通过 JavaScript challenge”
“部分格式未获取”
C:\Users>yt-dlp https://www.youtube.com/watch?v=QwbU-0BVcms
[youtube] Extracting URL: https://www.youtube.com/watch?v=QwbU-0BVcms
[youtube] QwbU-0BVcms: Downloading webpage
WARNING:
[youtube] No supported JavaScript runtime could be found.
Only deno is enabled by default;
to use another runtime add
--js-runtimes RUNTIME[:PATH]
to your command/config.
YouTube extraction without a JS runtime has been deprecated,
and some formats may be missing.
See https://github.com/yt-dlp/yt-dlp/wiki/EJS
for details on installing one
我很好奇 JS challenge 到底在 challenge 什么?
可能很多人的直觉是:
网站给出一道 JavaScript 题,让客户端计算出答案,然后服务器验证答案是否正确。
但实际情况完全不是这样。
JS challenge 表面上是在算题,本质上是在判断:
访问网站的到底是一个真实浏览器,还是自动化程序。
一、JS challenge 的基本流程
当浏览器访问某些网站时,服务器不会立刻返回真正的网页,而是先返回一段 JavaScript 代码。
浏览器需要:
- 下载这段 JavaScript
- 执行代码
- 生成一个 token 或 cookie
- 再次请求页面
服务器验证通过之后,才会返回真正的内容。
整个过程通常只需要几十毫秒,用户几乎感觉不到。
但如果访问者是:
- curl
- wget
- 简单爬虫
- 某些下载工具
这些程序通常不会执行 JavaScript,于是 challenge 就会失败。
二、为什么要这样做
网站这样做的目的主要有两个:
第一,阻挡爬虫。 第二,减少自动化攻击。
很多网站每天都会遇到大量自动化请求,例如:
- 爬取数据
- 批量注册
- 暴力登录
- 抢购脚本
如果服务器直接返回网页,这些程序就可以很轻松地获取数据。
JS challenge 的作用,就是在访问入口增加一个过滤器。
只有能够正常执行 JavaScript 的环境,才会被认为是比较可信的访问者。
三、为什么看起来只是“算一道数学题”
很多 JS challenge 的代码里,会出现一些类似这样的计算:
var a = 37
var b = 19
var c = (a * b + 17) % 97
看起来好像只是一个简单的数学题。
但实际上,这些计算只是外壳。
在这些计算的过程中,脚本通常会顺便读取很多浏览器信息,例如:
浏览器类型 操作系统 屏幕分辨率 时区 字体 WebGL 信息 Canvas 指纹
这些信息组合起来,可以形成一种叫做浏览器指纹的特征。
服务器并不是单纯验证数学结果,而是在判断:
这些数据看起来是否像一个真实浏览器生成的。
四、为什么很多下载工具会失败
像 yt-dlp 这样的工具,主要是通过 HTTP 请求直接获取网页数据。
但 JS challenge 的逻辑是:
必须执行 JavaScript 才能获得访问凭证。
如果工具没有完整的浏览器环境,就会出现问题,例如:
没有 screen 对象 没有 canvas 没有 webgl 没有真实的 navigator 信息
即使某些工具尝试模拟这些环境,也很难做到完全一致。
因此服务器就会判定 challenge 失败。
于是工具就拿不到真正的页面数据,也就无法解析出所有的视频格式。
五、为什么 challenge 的代码经常看起来很乱
很多 JS challenge 只有几十 KB,但代码看起来非常复杂,变量名也很奇怪,比如:
_0x3a21 _0x7f9c
这是因为这些代码通常经过了混淆处理。
混淆的目的不是提高运行效率,而是增加分析难度。
通过:
随机变量名 字符串加密 控制流打乱 无意义计算
可以让代码非常难以阅读。
对普通浏览器来说,这些代码只需要执行即可; 但对试图逆向算法的程序来说,理解成本会非常高。
六、challenge 真正检测的是什么
如果把所有细节总结起来,JS challenge 实际上在检测的是:
JavaScript 是否能正常执行 浏览器环境是否完整 浏览器指纹是否合理 执行时间是否正常 行为模式是否像真人
换句话说,它并不是在挑战计算能力,而是在挑战:
来访者是不是真正的浏览器。
扩展阅读: