WebGL 指纹识别:控制 GPU 身份与渲染器
WebGL 渲染器字符串和渲染输出如何暴露 GPU 身份。学习引擎级技术控制 WebGL 指纹信号。
简介
WebGL(Web 图形库)在浏览器中实现硬件加速的 3D 渲染。网站将其用于游戏、数据可视化、地图和交互内容。但 WebGL 也暴露了关于 GPU 的详细信息:供应商名称、渲染器字符串、支持的扩展、精度格式以及渲染操作的像素级输出。追踪系统收集这些值来构建高度稳定且难以更改的 GPU 指纹。因为 GPU 配置在设备间差异很大,WebGL 指纹识别即使在其他信号相同的情况下也能区分用户。本文解释 WebGL 指纹识别的工作原理、为什么常见保护方法不够,以及 BotBrowser 如何在渲染引擎级别控制 GPU 身份。
隐私影响
WebGL 指纹识别是可用的最强大追踪信号之一。Inria 和鲁汶天主教大学的研究发现,仅 GPU 渲染器字符串就能将用户群体缩小到少于 100 人的组。当与渲染输出结合时(绘制特定场景并回读像素数据),唯一性大幅增加。
伊利诺伊大学 2020 年的研究表明,WebGL 渲染产生设备特定的输出,因为 GPU 以微小的硬件级差异实现浮点算术、纹理采样和着色器执行。来自不同制造商的两个 GPU,即使运行相同的着色器代码,也产生视觉上相似但数值上不同的帧缓冲区。
部署规模很大。根据普林斯顿 Web 透明度和问责项目,WebGL 指纹识别脚本在 Alexa 排名前 10,000 的网站中超过 7% 上被发现。该技术已成为商业追踪库中的标准组件,与 Canvas、音频和字体指纹识别一起使用来构建复合标识符。
WebGL 指纹通过三条独立的采集面到达 GPU。下图展示跟踪方从每条采集面拿到什么,以及三股数据如何汇成一个复合标识符。
第三个采集面才是 WebGL 指纹难破的真正原因。字符串和参数可以用 JavaScript 改写,但 readPixels 输出来自真实 GPU 计算。一个返回"Mac GPU vendor"字符串、却让 framebuffer 带着"Intel iGPU"像素特征的方案,会被任何同时哈希两侧并比对的交叉检验秒杀。
技术背景
WebGL 通过多种机制暴露 GPU 身份。
渲染器和供应商字符串
最直接的暴露来自 WEBGL_debug_renderer_info 扩展:
gl.getParameter(debugInfo.UNMASKED_RENDERER_WEBGL)
// "ANGLE (NVIDIA GeForce RTX 3080, D3D11)"
gl.getParameter(debugInfo.UNMASKED_VENDOR_WEBGL)
// "Google Inc. (NVIDIA)"
这些字符串揭示了 GPU 制造商、型号,有时还有图形驱动版本。它们的唯一性足以显著缩小用户群体。
渲染输出
WebGL 渲染输出因硬件差异而在像素级别变化。指纹识别脚本可能绘制一个具有特定着色器、渐变和透明度的复杂场景,然后用 readPixels() 回读帧缓冲区。生成的像素数据被哈希以产生稳定的标识符。
渲染变化的来源包括:
- 浮点精度。 不同 GPU 以不同的舍入行为处理浮点运算,特别是在片段着色器中。
- 纹理过滤。 双线性和三线性过滤实现在供应商间不同。
- 抗锯齿。 默认 MSAA 实现在 GPU 架构间不同。
- 驱动级优化。 GPU 驱动应用影响像素输出的供应商特定优化。
参数查询
WebGL 通过 getParameter() 暴露数十个能力参数:
MAX_TEXTURE_SIZE、MAX_VIEWPORT_DIMS、MAX_RENDERBUFFER_SIZEMAX_VERTEX_ATTRIBS、MAX_VARYING_VECTORS、MAX_FRAGMENT_UNIFORM_VECTORSALIASED_LINE_WIDTH_RANGE、ALIASED_POINT_SIZE_RANGE- 支持的压缩纹理格式、着色器精度格式和扩展
每个值反映 GPU 的能力并对整体指纹做出贡献。
WebGL2 扩展
WebGL2 增加了额外的暴露面。MAX_3D_TEXTURE_SIZE、MAX_ARRAY_TEXTURE_LAYERS 和 MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS 等参数进一步区分硬件。getShaderPrecisionFormat() 方法返回每种着色器类型的精度细节,在 GPU 系列间不同。
下图显示同一段 WebGL 测试代码在四种常见 GPU 上产生的不同指纹。每一行都是真实部署里跟踪方在数据库中能看到的样本。
常见保护方法及其局限性
完全阻止 WebGL 防止了指纹识别但破坏了大量现代 Web。地图、3D 产品查看器、数据仪表板和游戏都依赖 WebGL。禁用它使你的浏览器功能更少,也创建了独特的指纹信号:WebGL 的缺失本身就是不常见且可追踪的。
伪装渲染器字符串可以改变 UNMASKED_RENDERER_WEBGL 返回的内容,但这只解决了指纹的一部分。实际渲染输出仍然来自真实 GPU。网站可以绘制测试场景、回读像素,并从渲染特征确定你的实际 GPU 类别。报告的字符串和渲染行为之间的不匹配本身就是强信号。
向 readPixels 输出添加噪声修改了返回的帧缓冲区数据。但噪声注入是可检测的:两次渲染相同场景应产生相同输出。如果不同,说明存在噪声注入。
使用软件渲染(如 SwiftShader 或 Mesa llvmpipe)在硬件间产生一致的输出,但创建了自己独特的指纹。软件渲染器具有识别它们的特征精度行为和参数值。
核心挑战在于 WebGL 指纹识别结合了报告的参数和实际渲染输出。有效的保护必须同时控制两者。
BotBrowser 的引擎级方法
BotBrowser 在 Chromium 渲染引擎级别控制 WebGL 身份,确保报告的参数和实际渲染输出都与加载的配置文件一致。
下图把四种常见 WebGL 防御方案与现代指纹识别系统的交叉验证逻辑放在一起对比。引擎层之上的方案都会在字符串、参数和像素之间留下可测量的不一致。
GPU 身份控制
当加载指纹配置文件时,BotBrowser 配置 WebGL 子系统报告配置文件 GPU 的身份:
- 渲染器和供应商字符串匹配目标设备。Intel UHD 630 系统的配置文件报告该 GPU 的精确渲染器和供应商字符串。
- 参数值(最大纹理大小、视口尺寸、精度格式等)都匹配配置文件 GPU 的能力。这些不是随机值。它们对应真实硬件配置。
- 扩展列表报告配置文件 GPU 支持的精确扩展。与真实设备相比,不添加也不移除扩展。
渲染输出一致性
BotBrowser 的配置文件系统包含目标 GPU 渲染特征的信息。与 --bot-noise-seed 标志结合时,渲染输出变得确定性且与配置文件设备一致。引擎应用受控变化,产生看起来真实的像素数据而不暴露你的实际 GPU。
WebGL2 覆盖
相同的控制适用于 WebGL2。参数、扩展和渲染行为都从配置文件派生。WebGL1 和 WebGL2 保护之间没有差距。
跨 API 一致性
BotBrowser 确保 WebGL 数据与其他配置文件信号一致。WebGL 报告的 GPU 与 navigator.userAgentData 中出现的、Canvas 渲染产生的以及 WebGPU 报告的(如果启用)一致。这种跨 API 一致性至关重要,因为追踪系统通常跨多个 API 交叉检查 GPU 相关信号。
配置和使用
基本 WebGL 保护
加载配置文件以配置所有 WebGL 参数:
chrome --bot-profile="/path/to/profile.enc" \
--user-data-dir="$(mktemp -d)"
WebGL 配置覆盖
独立控制 WebGL 行为:
# 使用配置文件的 WebGL 设置(默认)
chrome --bot-profile="/path/to/profile.enc" \
--bot-config-webgl=profile
# 使用真实系统 GPU(无 WebGL 保护)
chrome --bot-profile="/path/to/profile.enc" \
--bot-config-webgl=real
# 完全禁用 WebGL
chrome --bot-profile="/path/to/profile.enc" \
--bot-config-webgl=disabled
使用噪声种子实现确定性渲染
chrome --bot-profile="/path/to/profile.enc" \
--bot-noise-seed=42 \
--user-data-dir="$(mktemp -d)"
Playwright 集成
const { chromium } = require('playwright');
const browser = await chromium.launch({
executablePath: '/path/to/botbrowser/chrome',
args: [
'--bot-profile=/path/to/profile.enc',
'--bot-config-webgl=profile',
'--bot-noise-seed=42'
]
});
const page = await browser.newPage();
await page.goto('https://example.com');
Puppeteer 集成
const puppeteer = require('puppeteer');
const browser = await puppeteer.launch({
executablePath: '/path/to/botbrowser/chrome',
defaultViewport: null,
args: [
'--bot-profile=/path/to/profile.enc',
'--bot-config-webgl=profile',
'--bot-noise-seed=42'
]
});
const page = await browser.newPage();
await page.goto('https://example.com');
单独控制 WebGL 图像噪声
# 禁用 WebGL 图像噪声同时保持其他 WebGL 保护
chrome --bot-profile="/path/to/profile.enc" \
--bot-config-noise-webgl-image=false
验证
渲染器字符串检查。 打开浏览器控制台查询 UNMASKED_RENDERER_WEBGL。值应匹配配置文件的目标 GPU,而不是你的实际硬件。
参数一致性。 检查几个 WebGL 参数(MAX_TEXTURE_SIZE、精度格式、扩展列表)并确认它们匹配配置文件 GPU 的预期值。
跨会话稳定性。 使用相同配置文件和噪声种子在两个会话中运行相同的 WebGL 指纹识别例程。结果应完全相同。
跨机器稳定性。 在不同硬件上使用相同配置文件和种子运行测试。WebGL 指纹应匹配。
访问 BrowserLeaks、CreepJS 或类似的指纹测试网站,将你的 WebGL 输出与配置文件的预期值进行比较。
下面这张验证矩阵展示同一配置文件在三台不同主机上跑出来的"成功状态"。无论底层硬件是什么,UNMASKED 字符串相同、WebGL1 和 WebGL2 的 readPixels 哈希相同、扩展列表也相同。
最佳实践
- 使用
--bot-config-webgl=profile(默认)。 这确保所有 WebGL 参数来自配置文件。只在特别需要原生 GPU 访问时切换到real。 - 与
--bot-noise-seed结合以获得确定性输出。 没有种子,WebGL 渲染噪声在会话间变化。固定种子确保可复现性。 - 匹配 WebGL 和 WebGPU 设置。 如果配置文件同时包含 WebGL 和 WebGPU 数据,将两者都保持在
profile以保持一致性。WebGL 和 WebGPU 报告的 GPU 不匹配是可检测的不一致。 - 不要手动覆盖渲染器字符串。 BotBrowser 通过配置文件处理这个。手动设置渲染器字符串而不匹配渲染行为会产生不一致。
- 使用指纹检查网站测试。 在部署到生产之前验证你的 WebGL 指纹符合预期。
常见问题
Q: 没有 WEBGL_debug_renderer_info 扩展,WebGL 指纹识别还有效吗? A: 部分有效。没有该扩展,渲染器和供应商字符串不可用,但渲染输出、参数值和精度格式仍然因 GPU 而异。仅基于渲染的指纹通常足以缩小用户群体。
Q: 我可以在 WebGL 密集型应用如 3D 游戏中使用 BotBrowser 吗? A: 可以。BotBrowser 的 WebGL 控制在身份和指纹级别操作。渲染性能来自你的实际 GPU。游戏和 3D 应用正常工作。
Q: WebGL 保护是否影响 Canvas 指纹识别? A: Canvas 2D 和 WebGL 使用不同的渲染路径,但 BotBrowser 通过配置文件控制两者。它们产生与相同配置文件设备一致的结果。
Q: 如果网站请求不在我的配置文件中的 WebGL 扩展会怎样? A: BotBrowser 只报告配置文件中列出的扩展。不支持扩展的请求返回 null,与配置文件 GPU 的行为一致。
Q: WebGL2 是否与 WebGL1 分开保护?
A: 两者都由相同的配置文件和配置控制。--bot-config-webgl 标志同时适用于 WebGL1 和 WebGL2 上下文。
Q: BotBrowser 如何处理 Web Workers 中的 WebGL? A: Workers 中的 OffscreenCanvas WebGL 上下文受到与主线程上下文相同的引擎级控制。指纹在两者间保持一致。
总结
WebGL 指纹识别结合了 GPU 身份字符串、能力参数和像素级渲染输出来创建强大的追踪信号。因为它深入硬件层,基于 JavaScript 的保护无法完全解决它。BotBrowser 在 Chromium 引擎级别控制 WebGL 身份和渲染,确保每个 API 面返回与加载的指纹配置文件一致的值。结合 --bot-noise-seed 实现确定性输出和 --bot-config-webgl 实现显式控制,BotBrowser 提供完整的 WebGL 指纹保护。
相关主题请参阅什么是浏览器指纹识别、WebGPU 指纹保护、Canvas 指纹识别和音频指纹保护。