WebGL 不仅用于炫酷的网页特效,还能通过 GPU 渲染差异精准追踪用户。深入了解 WebGL 指纹如何提取显卡与驱动信息,以及有哪些有效的防范手段。
WebGL 指纹通过查询你的显卡硬件、并观察它渲染 3D 场景的具体方式来识别设备。由于显卡型号、驱动版本和操作系统的每一种组合都会产生略有差异的输出、并报告略有不同的能力参数,网站无需任何授权、Cookie 或本地存储,就能读取你的 GPU 厂商与渲染器字符串、枚举支持的扩展和数值上限,并把渲染出的图像哈希成一个稳定的标识。这使得 WebGL 成为现代浏览器指纹中信息熵最高的信号之一。
WebGL 是什么,为何会暴露硬件身份
WebGL 是一套 JavaScript API,让网页能够低层级地访问 GPU,实现硬件加速的二维和三维图形渲染。它本质上是浏览器对 OpenGL ES 的绑定,这意味着当你调用 WebGL 时,请求会穿过浏览器、穿过图形驱动,一直抵达机器内部那块物理显卡。
正是这种深层访问让它成为指纹采集的富矿。为了高效绘图,WebGL 必须暴露硬件的能力——支持多大的纹理、有哪些可选特性、着色器如何对浮点数取整。同时它产生的输出也因硬件而异:同一段绘制指令,在 NVIDIA 显卡、苹果 GPU 和 Intel 集成显卡上会得到略有差别的像素,因为它们对光栅化、抗锯齿和浮点运算的实现各不相同。
WebGL 信号大致分为两类:声明型元数据(显卡自我报告的字符串和数值)和渲染输出(实际绘制场景时产生的像素)。两者共同补充了Canvas 指纹检测所探测的二维渲染路径,也一起汇入我们在浏览器指纹完整指南中描述的整体画面。
高信息量的数据点
显卡厂商与渲染器字符串
WebGL 中辨识度最高的单项信号,是未屏蔽的厂商与渲染器字符串。默认情况下标准的 VENDOR 和 RENDERER 参数只返回通用值,但 WEBGL_debug_renderer_info 扩展会暴露真实的硬件身份——例如 Google Inc. (NVIDIA) 以及 ANGLE (NVIDIA, NVIDIA GeForce RTX 3070 Direct3D11 vs_5_0 ps_5_0, D3D11) 这样的字符串。
这条字符串往往会透露你的 GPU 型号、渲染后端(Direct3D、Metal、或经由 ANGLE 的 OpenGL),有时还包括驱动信息。仅凭它就能把你缩小到一小撮用户之中,因为精确的「GPU+后端」组合远比人们想象的更稀有。
这条字符串现在是否真的可读,很大程度上取决于浏览器。在默认的 Chrome 或 Edge 中依然可读;Brave 和 Firefox 则越来越不然——具体的逐浏览器现状见下文「浏览器现在隐藏了什么」。
支持的扩展列表
WebGL 自带一组核心特性,外加数十个可选扩展。getSupportedExtensions() 返回的列表——以及它的排列顺序——取决于显卡、驱动和浏览器版本。两台显卡型号相同的设备,如果驱动或浏览器构建不同,这里仍可能出现差异,从而又增加一层区分度。这也让它成为基于哈希的指纹采集的常用手段:脚本不需要解析这份列表,只需对它取哈希——一个稳定的哈希值和字符串本身一样好用,而这正是 Brave 现在着手攻击的特性(见下文「浏览器现在隐藏了什么」)。
数值参数与着色器精度
WebGL 暴露了一长串反映硬件能力的数值上限:MAX_TEXTURE_SIZE、MAX_VIEWPORT_DIMS、MAX_RENDERBUFFER_SIZE、走样线宽与点尺寸范围,以及顶点和片元着色器可用的最大 uniform 向量数。着色器精度——通过 getShaderPrecisionFormat() 查询高/中/低精度的浮点和整型格式——进一步增加了细粒度,因为精度行为与芯片本身紧密相关。
基于渲染的哈希
最强大的技术与 Canvas 指纹如出一辙,只是改由 GPU 驱动。脚本会渲染一个刻意设计得很「刁钻」的场景——渐变、光照、透明度、曲面几何——用 readPixels() 把像素读回并做哈希。不同 GPU 和驱动在浮点取整、抗锯齿和纹理过滤上的差异,会产生一个在硬件家族之间各不相同、但对同一设备保持稳定的哈希。由于它捕捉的是行为而非仅仅是声明值,要令人信服地伪造它要难得多。
代码层面的实际观察
下面的代码片段收集了指纹脚本会采集的核心 WebGL 信号。你可以通过 BrowserInsight 的指纹检测工具查看自己浏览器的等效实时结果。
function getWebGLFingerprint() {
const canvas = document.createElement('canvas');
const gl =
canvas.getContext('webgl') || canvas.getContext('experimental-webgl');
if (!gl) return { supported: false };
// 1. 通过 debug 扩展读取未屏蔽的 GPU 厂商与渲染器
const debugInfo = gl.getExtension('WEBGL_debug_renderer_info');
const vendor = debugInfo
? gl.getParameter(debugInfo.UNMASKED_VENDOR_WEBGL)
: gl.getParameter(gl.VENDOR);
const renderer = debugInfo
? gl.getParameter(debugInfo.UNMASKED_RENDERER_WEBGL)
: gl.getParameter(gl.RENDERER);
// 2. 数值能力上限
const params = {
maxTextureSize: gl.getParameter(gl.MAX_TEXTURE_SIZE),
maxViewportDims: gl.getParameter(gl.MAX_VIEWPORT_DIMS),
maxRenderbufferSize: gl.getParameter(gl.MAX_RENDERBUFFER_SIZE),
aliasedLineWidth: gl.getParameter(gl.ALIASED_LINE_WIDTH_RANGE),
};
// 3. 片元高精度浮点格式的着色器精度
const precision = gl.getShaderPrecisionFormat(
gl.FRAGMENT_SHADER,
gl.HIGH_FLOAT
);
// 4. 支持的扩展列表(存在与否、顺序都有信息量)
const extensions = gl.getSupportedExtensions();
return {
supported: true,
vendor,
renderer,
params,
precision: { rangeMin: precision.rangeMin, prec: precision.precision },
extensions,
};
}
基于渲染的哈希则更进一步:它编译一个着色器,绘制一个带光照的渐变图形,调用 gl.readPixels() 把结果读入一个类型化数组,再把该数组送入哈希函数。得到的值对你的设备保持恒定,却在不同硬件之间各不相同。
WebGL 增加了多少信息熵
信息熵衡量一个信号能把「你是谁」缩小到何种程度。WebGL 对追踪者格外有价值,恰恰因为它携带的信息熵很高且十分稳定:你的显卡不会在两次页面加载之间改变,大多数用户也从不更新驱动。厂商/渲染器字符串、扩展列表、数值上限和渲染哈希之间存在部分相关——它们都源自同一块硬件——但每一项都捕捉到了别项遗漏的细节。
实际上 WebGL 极少单独使用。它会与 Canvas、音频、字体、屏幕尺寸以及数十种其他属性组合在一起。下表对比了主要的 WebGL 信号类型,以及各自被伪造的难易程度。
| WebGL 信号 | 暴露的内容 | 稳定性 | 伪造难度 |
|---|---|---|---|
| 厂商/渲染器字符串 | GPU 型号+渲染后端 | Chrome/Edge 中极高;Brave 1.93+ 中为通用值,不具辨识度 | 易于覆盖,但会破坏一致性 |
| 支持的扩展列表 | 驱动+浏览器特性集 | 高;Brave 1.93+ 中按站点随机化 | 中等——列表须保持自洽 |
| 数值参数 | 硬件能力上限 | 极高 | 中等——各值相互关联 |
| 着色器精度 | 浮点运算行为 | 高 | 难——与真实芯片绑定 |
| 渲染像素哈希 | 真实的 GPU 渲染行为 | 高 | 极难——须伪造真实输出 |
由于元数据必须与渲染输出彼此吻合,粗糙的伪造往往反而增加唯一性:一台报告 Intel 渲染器字符串、却产出 NVIDIA 风格像素的设备,比一台老老实实如实上报的设备更扎眼。
浏览器现在隐藏了什么
以上信号描述的是默认的 Web 平台行为。各家浏览器正越来越多地偏离这一默认状态,截至 2026 年 8 月,「WebGL 理论上能读到什么」与「某个访客的浏览器实际返回什么」之间的差距,已经比以往任何时候都更大。最近、也最具体的例子是 Brave 的隐私更新 #38,它随 Brave 1.93 默认开启,覆盖 Brave 列出的两个平台:桌面端与 Android 端。Brave 说明这是历时数日的渐进式推送,因此并非所有用户都会立刻看到。它改变了前文提到的三项信号:
- 厂商与渲染器字符串被去标识化。 Brave 不再通过
WEBGL_debug_renderer_info返回你真实的 GPU 型号,而是把UNMASKED_VENDOR_WEBGL/UNMASKED_RENDERER_WEBGL替换成一个所有 Brave 用户共用的通用字符串。前文的描述在 Chrome 和 Edge 的默认配置下依然成立——但已不再适用于 Brave。 - WebGPU 适配器描述信息被清空。
GPUAdapterInfo——WebGL 厂商/渲染器字符串在 WebGPU 中的对应物,把vendor、architecture、device作为一等属性直接暴露、无需任何调试扩展——被 Brave 清空,而不是任由同样的硬件身份通过这个更新的 API 泄露。我们在《WebGPU 指纹:WebGL 之后的下一代 GPU 身份识别》中深入介绍了这一采集面;这里的简短结论是:Brave 1.93 用它关闭 WebGL 厂商字符串的同样方式,也关闭了这个口子。 - 扩展列表被随机化,而不只是被隐藏。Brave 会给
getSupportedExtensions()注入随机性,使基于哈希的指纹脚本在每个浏览会话、每个站点(eTLD+1)、每个存储区之间都看到不同的值。这直接击溃了前文描述的哈希采集手法:只有当同一份输入在你访问的每个网站上都产出相同哈希时,哈希才能充当有效的标识符。一旦底层的扩展列表本身就随站点而变,哈希也会随之变化——追踪者在 A 站点采集到的值,就不再与 B 站点采集到的值相符,即便你的硬件从未改变,它也彻底失去了作为跨站标识符的作用。
归属很重要,因为这些防护并非普适。Brave 在 1.93 及以后版本中默认执行去标识化与随机化。Firefox 走的是另一条路:开启 privacy.resistFingerprinting 后,它会直接禁用 WEBGL_debug_renderer_info,因此该模式下每个 Firefox 用户拿到的都是同一个通用的 VENDOR/RENDERER 值,而不是随机化的值。Chrome 和 Edge 默认既不去标识化也不随机化——未屏蔽的字符串和真实的扩展列表仍如前文所述那样可读。这并不意味着任何浏览器已经「解决」了指纹追踪问题,Brave 自己的发布说明也明确这仍是分阶段触达用户的过程——请把这一节视为「某一家厂商目前的防护现状」,而非「整个 Web 的现状」。
这样一条高辨识度的字符串,当初为何能在没有任何授权弹窗的情况下被读取,也值得了解一下。答案就写在扩展的名字里:WEBGL_debug_renderer_info 当初是为调试而制定的。与具体硬件相关的渲染 Bug 真实存在,一个绘制大量 3D 内容的网站,确实有正当理由去记录哪些 GPU/驱动组合会产出异常画面。然而一个扩展一旦向整个 Web 平台开放,任何网站都能调用它,而不仅仅是真正有排查需求的那些——这正是绝大多数无需授权、却高辨识度的指纹信号背后的共同模式。
网站如何使用 WebGL 指纹
正当用途很常见。反欺诈和反机器人系统会把 WebGL 指纹作为众多信号之一,用来发现自动化流量、无头浏览器,以及硬件突然变更的账户。分析与安全团队用它来标记可疑会话。无头和虚拟化环境常常暴露出标志性的渲染器字符串(例如 SwiftShader 或 llvmpipe 这类软件光栅器),这正是机器人检测如此倚重 WebGL 的原因。
同样的一致性逻辑不仅适用于 GPU,也延伸到浏览器本身:渲染器字符串必须与环境中的其他信息保持一致,包括实际绘制页面的渲染引擎。你可以使用 BrowserInsight 的浏览器内核检测查看自己的引擎及版本,并发现引擎伪装的迹象。
同样的能力也被用于跨站广告与追踪,因为一个基于硬件的稳定 ID 能在清除 Cookie 和无痕窗口中存活下来。正是这种双重用途,使得无论你关注的是安全还是隐私,理解 WebGL 指纹都很重要。
防御手段及其取舍
没有完美的防御——每种方案都会以牺牲功能、性能或「融入人群」的能力为代价。主要思路如下:
Tor 浏览器
Tor 浏览器走的是标准化路线:它出厂时配置统一,使所有用户呈现相同的指纹,并在允许 WebGL 之前先弹窗询问。目标不是隐藏你的显卡,而是让每个用户看起来都一模一样,这是最强的隐私模型——代价是速度下降以及部分 3D 内容失效。
Brave 与 Farbling
Brave 会对可指纹化的输出(包括 WebGL 读回结果)加入微小、确定性、且「按会话+按站点」变化的随机扰动(称为「farbling」)。每个站点看到的值都略有不同,且噪声会重新洗牌,因此难以构建稳定的跨站 ID,而大多数页面仍能正常工作。到了 Brave 1.93,这一思路也延伸到了元数据层——厂商/渲染器去标识化与扩展列表随机化的具体细节见上文「浏览器现在隐藏了什么」。
完全禁用 WebGL
通过开关或 webgl.disabled 彻底关闭 WebGL,会完全移除这一采集面。它有效但很「粗暴」:WebGL 的缺席本身就有几分不寻常,还会让地图、游戏和可视化工具失效。
数值伪造——以及它为何会适得其反
覆盖渲染器字符串或注入噪声的扩展可能有帮助,但确有风险。如果伪造的元数据与渲染像素或数值上限相互矛盾,成熟的检测器会察觉到这种不一致,于是你反而变得更易识别,而非更难。最安全的伪造应当一致、且为众多用户所共用;缺乏协同、各用户自行其是的随机化,常常事与愿违。
务实的结论是:选择一个被许多人共用的防御方案(Tor 的统一性、Brave 的 farbling),而不是一套让你变得独一无二的定制配置。想了解自己当前的处境,请运行 BrowserInsight 的指纹检测工具,查看你的 WebGL 渲染器、扩展和哈希。
WebGL 也并非 GPU 指纹采集的终点。更新的 WebGPU API 将适配器厂商、架构和设备字符串作为一等属性直接暴露——无需任何调试扩展。在不久前,它还是一个比 WebGL 更丰富、却少有隐私工具覆盖的采集面;Brave 1.93 通过清空这些适配器描述信息关闭了它,其他浏览器目前仍会如实暴露这些信息。
随机化不等于匿名
以上防御手段都无法让你彻底「隐形」,这一点值得说明白。一种让你的浏览器变得罕见的防御——一套没有其他人共用的随机化数值组合——可能和它原本想掩盖的信号一样具有辨识度,原因和「独自一人穿着奇装异服反而更显眼」一样。我们在《Cover Your Tracks:唯一性评分证明不了什么》中详细讨论了这种取舍及其应对思路。其背后的核心概念——需要多少人和你「长得一样」,一个信号才会失去重新识别你的能力——则在《浏览器指纹的信息熵与匿名集详解》中有完整解释。统一性(Tor,以及现在 Brave 去标识化的厂商字符串)之所以有效,是因为它让你始终身处一大群人之中;而按用户各自随机化,只有当足够多其他人也这么做时才真正有帮助。
常见问题
网站能读取我确切的显卡型号吗?
这取决于你的浏览器。WEBGL_debug_renderer_info 扩展会暴露一条未屏蔽的渲染器字符串,常常点出你的 GPU 型号和渲染后端,在默认的 Chrome 或 Edge 配置下,它依然无需任何授权弹窗即可被读取。Firefox 在开启 privacy.resistFingerprinting 后会禁用该扩展;而截至 Brave 1.93(2026 年 8 月),Brave 会把它替换成所有 Brave 用户共用的通用字符串——完整的逐浏览器现状见上文「浏览器现在隐藏了什么」。
隐私/无痕模式下 WebGL 指纹仍然有效吗?
是的。无痕浏览会清除 Cookie 和历史记录,但不会改变你的硬件。WebGL 信号——厂商、渲染器、扩展、数值上限和渲染哈希——在普通窗口和无痕窗口中保持一致,因此能在两者之间重新识别你。
禁用 WebGL 足以阻止指纹追踪吗?
它移除了 WebGL 这一采集面,但并未消除整体的指纹追踪。Canvas、音频、字体、屏幕等其他信号依旧适用,而 WebGL 的显眼缺席本身也可能成为一种区分特征。禁用它是一层防护,而非完整方案。
WebGL 指纹与 Canvas 指纹有何不同?
Canvas 指纹探测的是二维渲染路径,而 WebGL 探测的是由 GPU 驱动的三维路径,并额外暴露显式的硬件元数据(厂商、渲染器、能力上限)。两者相关但不同,这正是追踪者会同时采集二者以提高置信度的原因。


