跳到正文

机器人检测的原理——以及 Clearcote 为何直接修改引擎

常见做法是通过脚本修改 navigator.webdriver、伪装 WebGL 厂商、覆盖 navigator.plugins。检测方照样能识别出来——原因是结构性的,而不是又漏掉了某个属性。

一致性的图示:彼此印证的独立信号共同拼出一个完整的身份。那两个没有连线的点——背后没有任何支撑的伪装值——正是交叉校验能读出的缝隙。

JavaScript 伪装会自我暴露

JS 伪装,就是让一个普通函数占据了原生函数本该在的位置。检测方不看返回值本身,而是检查返回这个值的函数是不是原生的:

破绽为什么能抓出 JS 伪装
toString 自我暴露原生方法字符串化后是 function get vendor() { [native code] };而被覆盖的函数字符串化后得到的是它自己的源码——一次 .toString() 就能抓出来。
属性描述符 / hasOwnPropertygetOwnPropertyDescriptor 会暴露被重新定义的属性;对被篡改的函数调用 hasOwnProperty('toString') 会返回 true,而原生函数返回的是 false。
错误 this 引发的 TypeError原生 getter 在接收者(receiver)不对时会抛出特定的 TypeError;简单的 shim 却毫无反应,而这种沉默本身就是信号。
跨 realm 重新获取检测方从新建的 iframe 或 Web Worker 中取出一个未经改动的 Function.prototype.toString,拿它来检查你的 getter——这个 realm 与你在主世界(main world)中打补丁的 realm 不同。结果返回的是你的源码。露馅了。

Clearcote 没有这样的一层。navigator.userAgent 的 getter 就是 C++ getter:它报告 [native code],因为它本来就是原生代码,在每个 realm 中都完全相同——主框架、iframe 和 worker 皆是如此。根本不存在可供检测的 JavaScript 劫持。

机器人检测的三个层面

现代反机器人系统会在三个不同的位置,读取三类结构迥异的表面。单一工具很少能把三者全部解决:

层面破绽在哪里修复Clearcote
A · 驱动 / 二进制cdc_ ChromeDriver 变量、WebDriver 协议暴露面直接驱动原始 CDP,不用 chromedriver✅ 普通的 Chromium 二进制——没有任何驱动残留
B · CDP 副作用Runtime.enable 泄露、注入的 init 脚本、在主世界中执行、自动化默认视口控制层 / CDP 客户端层✅ 引擎消除了 Runtime.enable 泄露,SDK 默认不注入任何脚本,启动默认值也避开了自动化默认视口(你的客户端在哪里运行它自己的脚本,由客户端自己决定)
C · 指纹表面canvas、WebGL、音频、字体、navigator、TLS——横跨主框架、iframe 和 worker引擎(C++),因为 JS 覆盖会自我暴露✅ 这正是 Clearcote

最关键的一点:伪装与真实之间没有缝隙

由于这些控制都在引擎内部,页面看到的 JavaScript 与其底层的网络握手来自同一个真实的 Chromium。不存在“伪装的 JS 叠在真实 TLS 之上”这种缝隙可供交叉校验抓取——而这恰恰是基于注入的工具露馅的典型方式。一个 --fingerprint 种子(seed)会在 canvas、WebGL、音频、字体和硬件各方面生成一台内部自洽的机器。底层的 TLS 与 HTTP/2 握手就是引擎自己的握手;它之所以一致,是因为身份画像(persona)报告的正是引擎的真实版本——不设置 brandVersion,就能保持这一点。

如果噪声本身成了破绽,就把它关掉——canvas 和 WebGL 会返回自然值,而身份伪装保持开启(无论开关与否,渲染出的音频和 client rects 都不会被扰动,AudioContext 的采样率/延迟标量也会继续跟随身份画像;原因见此):

javascript
await launch({ fingerprint: "u1", fingerprintNoise: false });   // identity on, per-site farbling off

关闭噪声后,同一台机器上的各个身份会共用同一个 canvas 哈希。canvasNoise: false 只关闭 canvas 2D 噪声,保留 WebGL 噪声(授权版构建,150 r12+)。

网络与语言区域的一致性

身份画像只是一半:时区、语言和 WebRTC 地址也必须与流量的来源地相符。使用代理时,请带上 geoip 启动——SDK 会查询代理的出口 IP,按该地区设置时区、语言列表、地理位置和 WebRTC 地址;如果无法解析出地区,则中止启动。不使用它时,时区跟随语言(en-US 即对应纽约),与代理所在的国家无关。相关选项见 Playwright & Puppeteer。

这就是 Clearcote 修改 C++ 引擎、而不是注入 JavaScript 的原因。你能控制哪些内容,见指纹参数;引擎如何让所有信号整体保持一致,见架构。