一份中立的检测视角清单:TLS、Canvas 噪声、字体、UA-CH 等 12 个信号,分辨真实浏览器与反检测配置文件。
反检测浏览器和真实浏览器可以发送完全相同的 User-Agent 字符串,却在检测系统眼中截然不同。差距不在任何单一的请求头或 API 上——而在于一致性:真实设备是一个集成系统,而伪造的配置文件是由替换值拼凑而成,很难做到端到端彼此吻合。这是一份检测视角的清单,而非规避指南:12 个检测系统如今确实在使用的具体信号,按其所在的层级分组,并说明当声称的身份与背后的流量不一致时,每个信号会暴露什么。
核心要点
- 检测很少依赖单一信号——它评估的是数十个独立值彼此之间是否一致,而不仅仅是任何单个值看起来是否伪造。
- 网络层信号(TLS 握手、HTTP/2 帧)在页面加载之前就会被检查,对基于 JavaScript 的伪造工具而言完全不可见。
- 渲染层信号(Canvas、WebGL、字体)来自真实硬件和驱动程序,这使其成为最难令人信服地伪造的一层。
- 身份层信号(UA-CH、时区、语言环境)检测成本低,能抓住最常见、最粗心的伪造尝试。
- 目前没有任何反检测配置能同时通过所有层级的检查——修复一处不一致,往往会暴露另一处。
12 个信号一览
| # | 信号 | 真实浏览器会呈现什么 |
|---|---|---|
| 1 | TLS ClientHello(JA3/JA4) | 与声称浏览器的 TLS 库完全匹配 |
| 2 | HTTP/2 帧与请求头顺序 | 匹配声称浏览器的技术栈,而非脚本库 |
| 3 | Canvas 噪声重测 | 同一页面加载内重复绘制的哈希完全一致 |
| 4 | WebGL 渲染字符串与参数 | GPU 字符串与扩展、精度、纹理上限一致 |
| 5 | 字体列表与 GPU/操作系统 | 字体集合与声称的操作系统匹配 |
| 6 | 屏幕、触摸、硬件并发数 | 设备类别(手机、笔记本、桌面)内部一致 |
| 7 | UA-CH 与旧版 User-Agent | navigator.userAgentData 仅存在于 Chromium,且与 UA 字符串一致 |
| 8 | 时区与 IP 地理位置 | 本地时区与 IP 解析出的地区匹配 |
| 9 | 语言环境 / Accept-Language 与 IP | 浏览器语言与访客所在地区大致相符 |
| 10 | navigator.webdriver 与自动化残留 | 不存在,且全局作用域中没有 CDP 或驱动残留 |
| 11 | 属性描述符 / 原型完整性 | 原生 API 的 .toString() 输出和原型链未被篡改 |
| 12 | 无头渲染异常 | 权限状态一致,编解码器列表完整,没有纯软件渲染的 GPU |
网络层信号
1. TLS ClientHello——JA3/JA4
在页面加载之前,浏览器会通过一条 ClientHello 消息协商 HTTPS,其中列出的密码套件、扩展和椭圆曲线的顺序由 TLS 库决定,而非任何你能调整的设置。JA3 和 JA4 将该消息哈希为一个与底层库绑定的稳定指纹——Chromium 用 BoringSSL,Firefox 用 NSS。一个把 User-Agent 伪装成"Firefox"的反检测浏览器,如果底层其实是 Chromium 分支,交出的仍然是 Chromium 的 TLS 握手,因为更改 ClientHello 需要替换 TLS 库本身,而不是 JavaScript 层。参见TLS 指纹技术详解,了解 JA3、JA4 的计算方式,以及为什么 GREASE——RFC 8701 定义的保留占位值——会让简单的指纹匹配变得复杂。
2. HTTP/2 帧与请求头指纹
再往上一层,HTTP/2 连接自带其特征:SETTINGS 帧的值、流优先级行为,以及伪请求头(:method、:path、:authority)的顺序,在不同浏览器引擎和 HTTP 客户端库之间各不相同。一个 TLS 握手正确、但底层由自动化库驱动的配置文件,仍可能泄露不匹配的 HTTP/2 指纹,因为这两层由不同的代码路径实现,很少被一起打好补丁。
渲染层信号
3. Canvas 噪声重测差异
Canvas 指纹之所以有效,是因为微小的像素差异来自真实的 GPU 和驱动程序行为——而这种输出对给定设备而言是确定性的:在同一次页面加载中两次绘制相同的图形会得到相同的哈希。反检测工具通过在每次渲染时注入随机噪声来防御 Canvas 追踪,但这种噪声本身是可检测的:两次绘制同一 Canvas 并比较即可。真实浏览器总是匹配;逐次调用的噪声注入器则很可能产生两个不同的哈希。EFF 的 Cover Your Tracks 项目记录了 Canvas 唯一性在实践中是如何被测量的。
4. WebGL 渲染字符串与下游参数的匹配
未遮蔽的 WEBGL_debug_renderer_info 扩展暴露真实的 GPU 厂商和渲染字符串。替换成一个看起来合理的字符串很容易;让所有依赖该 GPU 的参数都与之吻合却很难——支持的扩展、最大纹理尺寸、着色器精度都取决于真正执行渲染的硬件,而非脚本声称的字符串。声称的 GPU 与渲染出的像素输出不匹配,是最强的信号之一,因为伪造它需要模拟整个 GPU 的行为,而不只是覆盖一个属性。
5. 字体列表与 GPU/操作系统的一致性
已安装的字体在很大程度上由操作系统决定:Windows 自带 Segoe UI 和 Calibri,macOS 自带 San Francisco 和 Helvetica Neue,每个 Linux 发行版也有自己的默认字体。一个声称是 macOS、却带着 Windows 字体列表的配置文件——或者一个只在 Windows 上出现的 GPU 渲染字符串,却搭配了仅限 Apple 的字体集——无需知道"正确"的指纹应该是什么样,就已经暴露了不一致。
6. 屏幕、触摸与硬件并发数
设备的屏幕尺寸、触摸事件支持情况和报告的 CPU 核心数,理应描述同一类硬件。一个"移动版 Safari"的身份却没有触摸事件、报告桌面级的核心数,不符合任何一款真实 iPhone 的特征。这些检查成本很低,这也正是粗糙的伪造工具依然会在这些地方被抓的原因。
身份层信号
7. UA-CH 与旧版 User-Agent
Chromium 浏览器通过 navigator.userAgentData 暴露结构化的 User-Agent Client Hints——这是一个 JavaScript 可读的对象,无论旧版 User-Agent 字符串声称什么,它要么存在(Chromium),要么不存在(Firefox、Safari):
// Chromium:返回一个对象。Firefox/Safari:undefined —— 无论 UA 字符串被伪造成什么。
console.log(navigator.userAgentData);
一个"Safari"身份如果暴露出一个已填充的 userAgentData 对象,那就明确是运行在 Chromium 之上。这是检测 User-Agent 伪造时最快的检查之一——完整的请求头与 client hints 层面的破绽,参见如何检测 User-Agent 伪造。
8. 时区与 IP 地理位置
每个浏览器都通过 Intl.DateTimeFormat().resolvedOptions().timeZone 暴露其本地时区。检测系统会将其与访客 IP 地址解析出的时区进行比对。一个声称在东京浏览、本地时区却报告 America/New_York 的配置文件,是一个立即触发的危险信号——而且修正成本很低,这也是为什么较为粗糙的反检测配置仍然会在这一点上失手。
9. 语言环境与 Accept-Language 与 IP 地区
类似地,Accept-Language 请求头和 navigator.language 理应与 IP 解析出的地区大致相符。一个越南住宅 IP 搭配 Accept-Language: ru-RU 请求头并非不可能——真实的旅行者和外派人士确实存在——但它会引发和时区不匹配同样的怀疑评分,尤其是叠加其他不一致时。
自动化与篡改信号
10. navigator.webdriver 与自动化残留
当一个会话由 WebDriver 协议驱动时,标准化的 navigator.webdriver 属性会被设为 true。除了这一个标志之外,自动化框架还会在全局作用域中留下残留——注入的 CDP 工件、window 上异常的属性,以及粗糙的伪造层忘记清理的驱动专属变量。更完整的自动化破绽清单参见Bot 检测技术。
11. 属性描述符与原型完整性
反检测浏览器通过在原型层拦截 API 来修改指纹值——重新赋值 HTMLCanvasElement.prototype.toDataURL,或包装 WebGLRenderingContext.prototype.getParameter。原生方法的 .toString() 返回 "function toDataURL() { [native code] }";被粗糙修改过的版本往往不会,而 Object.getOwnPropertyDescriptor() 即使在精密工具试图合成一个可信的原生代码标记时,也能揭露这种替换。
12. 无头渲染异常
无头和虚拟化的浏览器环境渲染页面的方式与普通桌面会话不同:permissions API 报告相互矛盾的状态、编解码器列表被截断,或者 WebGL 渲染字符串指向像 SwiftShader 这样的软件光栅化器,而非真实 GPU 硬件。这些异常不需要与声称的身份比对——它们本身就内部不一致。
伪造能隐藏什么,不能隐藏什么
伪造单一属性——一个 User-Agent 字符串、一个 Canvas 哈希、一个时区——很简单。让这十二个信号持续彼此一致,并在浏览器和操作系统更新中保持一致,则完全是另一回事。这与浏览器指纹本身背后的一致性原理相同,只是反过来读:检测系统衡量的不是指纹有多独特,而是它有多内部一致。修复成本最低的信号——时区、语言环境、旧版 UA 字符串——会被任何有能力的操作者最先修好。而那些根植于硬件和网络栈的信号——TLS 库、GPU 渲染出的实际输出、JavaScript 引擎行为——依然是持久的破绽,因为伪造它们意味着替换真正产生这些信号的组件本身,而不只是它报告的值。
检查你自己的设置
BrowserInsight 的指纹检测会并排呈现你的 Canvas 哈希、WebGL 渲染字符串和字体列表,Bot 检测工具则展示检测系统会评分的自动化与无头信号。如果你在运行多配置文件或强化隐私的设置,同时检查这两者是发现十二个信号中是否有任何一个与你所呈现的身份不符的最快方式。
常见问题
单一信号足以证明浏览器被伪造了吗?
单独看很少能。检测是概率性的——每一处不匹配都会提高怀疑评分,而系统是把多个信号综合起来判断,而不是依赖某一项绝对检查。一个有能力的操作者可以修好任意单个信号;难的是同时修好全部十二个,并在更新中持续保持修好状态。
这 12 个信号中哪个最难伪造?
渲染层和网络层的信号——WebGL 输出、Canvas 重测一致性,以及 TLS 握手——因为它们由硬件、驱动程序和 TLS 库产生,而非脚本可以直接覆写的值。要令人信服地伪造它们,意味着要复现整个组件的真实行为,而不只是编辑一个属性。
VPN 或代理会影响这些信号吗?
会间接影响时区和语言环境——如果出口位置与浏览器配置的语言环境不符;但普通的隧道式 VPN 不会触及 TLS、Canvas、WebGL 或 UA-CH,因为这些是由浏览器自身计算的,而非网络路径。与之并行运行的网络层检查,参见网站如何检测 VPN 与代理。
使用反检测浏览器违法吗?
不违法。反检测浏览器有合法用途——管理多个店铺、在平台规则允许的情况下运行并行广告账户,以及注重隐私的浏览。工具本身是中性的;检测系统关心的是它产生的信号是否内部一致,而不是操作者的意图。


