推荐设置——哪些该开启,哪些别去动
在我们追查到配置原因的拦截中,大多数是因为伪装得更多,而不是更少。本页是我们实测结果的精简版:值得使用的设置、得不偿失的设置,以及任何设置都解决不了的少数问题。
从这里开始
如果这一页你别的都不看,就从这份配置开始。它只改变那些可以安全改变的维度,渲染、TLS 和浏览器版本都保持原样。
import { launch } from "clearcote";
const browser = await launch({
lightStealth: true, // metadata identity only — rendering, TLS and version stay real
fingerprint: "account-42", // picks this identity's metadata bundle; keep it stable per identity
proxy: { server: "http://gateway:8080", username: "u", password: "p" },
geoip: true, // timezone + languages + the address WebRTC reports follow the proxy exit
humanize: true, // trusted native input; navigator.webdriver stays false
});Python 的接口完全相同,只是采用 snake_case:light_stealth、fingerprint、geoip、humanize。添加维度要有明确的理由,一次只加一个,并且只有当你能说清主机上有什么能佐证它时才添加。下文有几行——细粒度的 r12 控制项、使用录制动作轨迹的 humanize、socks5Udp、第三方 cookie 和代理相关的开关——适用于授权版构建(GitHub 免费版或 Pro)。开源版构建会忽略它们:humanize 会退回到生成的轨迹,而带密码的 SOCKS5 代理会拒绝连接。
原则:伪装就是一项需要主机来佐证的声明
检测并不是在寻找不寻常的值,而是在寻找矛盾——对同一台机器来说不可能同时成立的两个信号。一个没人伪装过的普通值,不会与任何东西矛盾。一个背后没有任何支撑的伪装值,则是一个阳性信号,而且这个信号比它所掩盖的那个值还要强。
所以,在启用任何选项之前,真正有用的问题不是“这个值合理吗?”,而是“这台机器上有什么能被拿来佐证它,得到的答案又是否一致?”屏幕尺寸会与渲染表面相互比对。GPU 字符串会与该 GPU 实际生成的像素相互比对。声称的操作系统会与该平台解析出的字体相互比对。如果主机无法为这项声明提供支撑,诚实的值反而更安全。具体机制参见检测的工作原理。
推荐这样做
| 设置 | 原因 |
|---|---|
lightStealth: true | 通过原生的单值开关,只应用一组由种子派生、彼此一致的元数据维度——hardwareConcurrency、deviceMemory、colorDepth、devicePixelRatio、maxTouchPoints。Canvas、WebGL 和音频输出、TLS 以及真实的浏览器版本都保持原样。种子只决定一个身份分到一小组配置包中的哪一个,而且没有渲染噪声,所以同一台机器上的各个账号共享同一个 canvas 哈希。在缩放过或不常见的显示器上,请在 audit 页面上核对配置包选出的像素比与你的屏幕是否相符。 |
每个身份使用一个固定的 fingerprint 种子 | 相同种子 ⇒ 相同身份。在账号的整个生命周期内一直使用它。每次请求都轮换种子,会让同一个已登录账号每次都像是从一台新机器上访问,这个信号远比任何被轮换的值都要明显。如果同一台机器上有多个账号,且它们不能共享同一个 canvas,请使用带种子的身份画像(去掉 lightStealth),让每个账号都有自己的渲染噪声。 |
每次启动都设置 geoip: true | 时区、语言以及 WebRTC 报告的地址都跟随出口 IP——无论是否使用代理(也可以自行设置 timezone 和 acceptLanguage)。两者都没有时,SDK 会默认使用纽约和 en-US。美国住宅出口却报告 Europe/Amsterdam,只需一行代码就能查出来,而这种不匹配又很容易无意中造成。如果无法解析出地区,启动会以 GeoipError 中止,而不会使用本机的时钟启动——请修复代理,或调高 CLEARCOTE_GEOIP_TIMEOUT_SECONDS。 |
humanize: true | 输入会作为受信任的原生事件生成,沿着类人的光标轨迹移动(在 Pro 上,坐标点击使用录制的真人动作,其他情况使用生成的轨迹)。不要在上面再叠加你自己的逐步缓动——设置好途经点,让 humanize 去插值;手写的缓动会产生原生输入从来不会产生的运动统计特征。 |
在 Chrome 品牌下设置 widevine: true | 默认身份画像以 Google Chrome 的身份呈现,而 Google Chrome 会响应 Widevine DRM 查询。没有 CDM 时,该查询会被拒绝,任何页面都能读出这一点。参见 Widevine & DRM。 |
嵌入式登录或验证出问题时,设置 allowThirdPartyCookies: true | 去 Google 化的底层会阻止第三方 cookie,而原版 Chrome 允许它们。依赖第三方 cookie 的嵌入式登录、支付和验证 iframe,就会以普通浏览器不会出现的方式失败(授权版构建,152 r22+)。 |
| 每个身份一个持久化 context | Cookie、站点存储和缓存都是身份的一部分。一台每次访问都像从未见过该站点的机器,本身就是一种特征模式。 |
| 在容器中借助 Xvfb 以有头模式运行 | 有头模式的 Chrome 直接避开了无头模式的各种破绽。官方镜像默认就是这样运行的。 |
| 在你打算声称的操作系统上运行 | CSS2 系统字体关键字由浏览器之下的平台来解析,而不是由页面或 user agent 所能控制的任何东西决定,因此即使更换 user agent,它们也不会变。如果做不到,请保持 Linux 字体包完整——见下文。 |
tlsProfile 保持为 match-persona | ClientHello 会跟随身份画像声称的 Chrome 版本,因此网络层与 UA 保持一致。默认值本来就是正确的;之所以要知道有这个选项,是为了避免把它固定成与你的品牌版本不一致的值。 |
避免这些做法
以下每一条都是我们实测中出过问题的情况,而不是泛泛的提醒。
| 避免 | 会出什么问题 |
|---|---|
在显示器对不上的主机上伪装 screen / avail* | 伪造的屏幕尺寸如果无法与真实窗口和渲染表面对上,在最严格的反机器人系统上就是一个稳定的拦截触发条件。这正是屏幕尺寸不属于 lightStealth、需要主动开启的原因。如果你确实要设置,就设置完整且一致的一整组——screenWidth、screenHeight、availWidth、availHeight——并留出一个合理的任务栏间隙,而不是让 avail 等于 screen。在无头模式下,SDK 已经会替你选择一个合理的屏幕。 |
| 在软件渲染的主机上声称拥有 GPU | 在容器中,或在通过 WARP、SwiftShader 或 llvmpipe 进行光栅化的云虚拟机上,声称拥有桌面 GPU 的身份画像描述的是一块芯片,而这块芯片生成不了页面能读回的那些像素。从授权版构建 150 r12 起(开源版构建会忽略它),gpuStringSpoof: false 会报告主机真实的 WebGL vendor 和 renderer,其他一切保持不变——在这类硬件上,这往往是更好的取舍。需要注意的是:WebGPU 仍然跟随范围更广的 disableGpuFingerprint,因此只用这个细粒度开关时,navigator.gpu 仍在描述身份画像,而 WebGL 报告的却是真实设备。 |
为了改一项设置而动用 disableGpuFingerprint 或 fingerprintNoise: false | 两者都是打包在一起的开关。disableGpuFingerprint 改变的是字符串,加上参数表,加上 readPixels 扰动,再加上身份画像字段屏蔽;fingerprintNoise: false 会把 canvas 2D 和 WebGL 噪声一起关掉。自授权版构建 150 r12 起有了细粒度控制:gpuStringSpoof: false 只针对字符串,canvasNoise: false 只针对 canvas 2D。这两个细粒度控制都有值得了解的代价:gpuStringSpoof: false 会改变 WebGL 字符串,但不会改变 navigator.gpu,于是同一页面上 WebGL 和 WebGPU 报出的厂商各不相同——分数好看,细看却经不起推敲。而 canvasNoise: false 与种子一起使用时,会彻底去掉每个账号各自的 canvas。请优先使用范围更广的 disableGpuFingerprint,它会让 WebGL 和 WebGPU 一起改变。 |
| 在会话中途轮换身份 | 在会话进行中更换种子、代理出口或时区,会与该会话此前的状态相矛盾。要在身份之间轮换,切勿在同一个身份内部轮换。 |
| 在 Linux 上使用 Windows 身份画像,却去掉了字体包 | Linux 版本自带度量兼容的替代字体(Segoe UI→Selawik、Arial→Arimo、Times New Roman→Tinos 等),SDK 会在启动时通过 FONTCONFIG_FILE 把它们接入。如果把它们去掉,或者构建一个丢弃了 <binDir>/fonts/ 的自定义镜像,身份画像声称的那些 Windows 字体族就会全部坍缩成同一个默认字体。这一点不需要任何参考数据就能看出来:Windows 自带的两种不同字体,测出来的尺寸却完全相同。 |
| 在上面再叠加一个 JS stealth 插件 | Clearcote 的各项控制是编译进引擎的。在其上再加一层脚本级的垫片(shim),恰恰重新引入了引擎方案本来要消除的那种自我暴露的层——被覆盖的 getter 字符串化后会显示它自己的源码,一次 toString() 就能读出来。 |
| 不加限定地在桌面引擎上使用移动端身份画像 | platform: "android" 会改变 UA、触控、指针类型、屏幕和视口,但像素仍然来自桌面 GPU(只是挂着移动 GPU 的名称),精细的页面几何特征也仍是桌面的。它只是一个尽力而为的身份画像;请把渲染表面当作你仍需自行解决的部分。 |
代理
代理对结果的影响通常比任何浏览器参数都大,却也是最常被随手留作默认值的那一项。
- 固定出口。每个身份一个出口 IP,并在整个会话中保持不变。一个在流程中途变更 IP 的会话会自相矛盾,无论指纹做得多好。
- 让地理位置与身份画像相匹配,然后让
geoip: true推导出时区和语言,而不是手动设置它们。 - 带凭据的代理交给引擎处理。HTTP(S) 代理的密码由浏览器自身来应答(授权版构建 151 r19+,Python & Node SDK),因此页面缓存保持开启,请求也不会因拦截而被打上标记。在授权版构建上,带密码的 SOCKS5 可以直接使用——把用户名和密码作为单独的字段传入即可。
transparentProxy: true(152 r22+)会在请求头和连接时序中隐藏代理的存在。 - 理解 WebRTC 的取舍。Chromium 从不通过 HTTP 或 SOCKS 代理发送媒体流,因此对等连接的流量原本会走你的真实网络连接,中继服务器就能从中读到你的真实地址。所以 SDK 会阻止不经代理直接发出的 WebRTC UDP 流量。在开源版构建上,
geoip/webrtcIp会解除这一阻止;授权版构建则会保留它。请搭配geoip: true(或webrtcIp)使用,这样页面仍能看到真实 Chrome 会产生的候选地址,其中报告的是代理的地址;否则 WebRTC 什么也收集不到,看起来就像被屏蔽了。如需通过 SOCKS5 代理建立可用的对等连接,请设置socks5Udp: true(授权版构建;你的代理服务商必须允许 UDP 中继),或者使用不设浏览器代理的全隧道(WireGuard/OpenVPN),并在args中传入你自己的--webrtc-ip-handling-policy。其他情况下,请保留默认的阻止策略。
设置解决不了的部分
有些最常被读取的指纹面,取决于你租用的机器,而不是你在上面运行的浏览器。
| 指纹面 | 由主机决定的内容 |
|---|---|
| 渲染 | 没有 GPU 的主机只能用软件进行光栅化。这一类产品都只能二选一:要么声称拥有无法渲染的硬件,要么承认自己用的是软件光栅化器。如果渲染出的像素对你的目标站点很重要,解决办法是换一台有 GPU 的主机——或者使用 canvas bridge,它会把渲染工作转发给一台有 GPU 的机器。 |
| 字体 | 字体列表受限于主机实际能绘制的字体——从授权版构建 150 r12 起,标准字体列表来自三千台真实机器,并且仍会与主机的字体取交集,因此页面不会被告知任何无法渲染的字体。在 Linux 上,SDK 使用随浏览器附带的字体包,并忽略主机上安装的字体,因此在你的镜像里安装软件包不会改变页面看到的内容。开源版构建的字体包不含 CJK 字体,所以中文、日文和韩文文本在其中会显示为缺失的字形;授权版构建的字体包则包含这些字体。在 Windows 上使用的是主机自身的字体。字体包里的 Windows 近似字体能让宽度看起来合理,但页面也可以用 local() 按名称加载字体,而近似字体并不带有真实的字体名:在 Linux 上用 Windows 身份画像实测时,Segoe UI 和 Georgia 被测为已安装,却无法加载。 |
| 字体缩放器 | 每个字形的尺寸都由操作系统的字体缩放器决定:Windows 上是 DirectWrite,Linux 上是 FreeType。任何设置都触及不到它。把字号按 0.01 px 逐步调整,在 Windows 上有 99% 的步进会改变文本宽度,在 Linux 上则是 66%;而一台以 Windows 身份呈现的 Linux 主机,测出来的仍然是 66%。如果页面会仔细读取你的文本,请在你所声称的操作系统上运行。参见 user agent 之下的字体栈。 |
去验证,而不是想当然
本页的每一条结论都可以在浏览器内部测量出来。把你配置好的会话指向 audit 页面,查看它标出不一致的那些行——这正是我们用来发现上述问题的同一套工具,它会告诉你当前的配置违背了本页的哪些建议。
相关阅读