跳到主要内容 深入浏览器指纹:Canvas、WebGL、Audio 如何暴露身份 | 极客日志
JavaScript 大前端 算法
深入浏览器指纹:Canvas、WebGL、Audio 如何暴露身份 探讨了浏览器指纹技术如何通过 Canvas、WebGL 和 Audio API 收集设备特征以唯一标识用户。文章详细解析了三大核心技术的原理与实现代码,分析了其难以防御的原因,并介绍了现代浏览器的反指纹防御策略如 Canvas Farbling。最后提供了开源库实战方案及针对开发者和普通用户的隐私保护建议。
机器人 发布于 2026/4/5 更新于 2026/4/13 1 浏览
你以为清除了 Cookie 就安全了?2025 年约翰霍普金斯大学的研究首次证实:浏览器指纹追踪比你想象的更普遍,而且你几乎无法阻止它。
背景:Cookie 时代的终结
还记得那些年困扰我们的 Cookie 弹窗吗?
'本网站使用 Cookie 改善您的体验'——然后给你两个选项:一个巨大的"接受所有 Cookie"按钮,和一个藏在角落里的"拒绝"链接。这就是所谓的"暗模式"(Dark Pattern),专门用来诱导用户同意追踪。
好消息是,这个时代正在落幕。Chrome、Firefox、Safari 都在逐步默认阻止第三方 Cookie。但坏消息是—— 。
广告商们找到了更隐蔽的武器:浏览器指纹
浏览器指纹最大的特点是:你无法删除它,甚至无法感知它 。它就像你在互联网上留下的无形签名,无论你如何清理浏览数据,它都能把你认出来。
2025 年 2 月,约翰霍普金斯大学和德州农工大学的研究团队发布了论文《The First Early Evidence of the Use of Browser Fingerprinting for Online Tracking》,首次实证证实了浏览器指纹被广泛用于广告追踪 。研究团队通过 FPTrace 框架发现,改变指纹后广告竞价出现了显著差异,直接证明了指纹与广告定向的关联。
更讽刺的是,2025 年 3 月,Google 修改了隐私政策,允许在 Privacy Sandbox 中使用浏览器指纹技术 。这意味着连倡导"隐私保护"的科技巨头,也在拥抱这种技术。
什么是浏览器指纹? 简单来说,浏览器指纹就是通过收集浏览器和设备的多种特征信息,生成一个几乎唯一的标识符。这些特征包括但不限于:
特征类别 具体信息 熵值贡献 User Agent 浏览器版本、操作系统 中等 屏幕信息 分辨率、颜色深度、可用分辨率 低 时区语言 时区偏移、首选语言 低 字体列表 已安装字体 极高 插件信息 浏览器插件列表 中等 Canvas 2D 渲染像素差异 极高 WebGL GPU 型号、渲染器信息 极高 Audio 音频处理特征 高 Hardware 内存、CPU 核心数 中等
根据 EFF 的 Panopticlick 研究,在 100 万个样本中,94.2% 的浏览器指纹都是唯一的。
打个比方:如果把 User Agent 比作你的名字,Canvas 指纹就是你的笔迹,WebGL 指纹是你的 DNA——前者很容易伪造,后者几乎无法复制。
Canvas 指纹:像素的秘密
原理剖析 Canvas 指纹是浏览器指纹中最成熟、最稳定的技术之一。它的核心思想非常简单:让浏览器在 Canvas 上绘制特定内容,然后读取像素数据,不同浏览器/设备产生的像素差异就是指纹 。
显卡驱动差异 :不同 GPU 渲染相同的图形会有细微差异
操作系统差异 :Windows、macOS、Linux 的字体渲染引擎不同
浏览器差异 :Chrome、Firefox、Safari 的 Canvas 实现有差异
抗锯齿算法 :不同浏览器使用不同的抗锯齿策略
实战代码 function getCanvasFingerprint ( ) {
const canvas = document .createElement ('canvas' );
const ctx = canvas.getContext ('2d' );
canvas.width = 200 ;
canvas.height = 50 ;
ctx.fillStyle = '#f60' ;
ctx.fillRect (0 , 0 , 200 , 50 );
ctx.textBaseline = 'alphabetic' ;
ctx.fillStyle = '#069' ;
ctx.font = '16px "Times New Roman"' ;
ctx.fillText ('FingerprintJS 🤓' , 10 , 30 );
ctx.strokeStyle = '#06f' ;
ctx.arc (150 , 25 , 15 , 0 , Math .PI * 2 );
ctx.stroke ();
const data = canvas.toDataURL ('image/png' );
return hashString (data);
}
真实案例 fingerprintjs (GitHub 26.4k stars)的 Canvas 实现更加复杂:
function renderTextImage (canvas, context ) {
const text = 'Cwm fjordbank glyphs vext quiz 😃' ;
context.font = '14px Arial' ;
context.fillText (text, 2 , 20 );
context.beginPath ();
context.moveTo (100 , 5 );
context.lineTo (120 , 35 );
context.stroke ();
}
function isCanvasStable (canvas ) {
const img1 = canvas.toDataURL ();
const img2 = canvas.toDataURL ();
return img1 === img2;
}
为什么难以防御? Canvas 指纹的可怕之处在于它利用了合法的 Web API 。网站可以说"我只是想画个图表",实际上却在偷取你的指纹。你无法完全禁用 Canvas,否则大量网站(包括图表库、游戏、视频编辑)都会失效。
WebGL 指纹:GPU 的指纹 如果说 Canvas 指纹是"笔迹",那 WebGL 指纹就是"DNA 检测"——它直接读取你的显卡型号和驱动信息。
原理剖析 WebGL(Web Graphics Library)是浏览器中的 3D 图形 API。它的指纹信息主要来源:
GPU 型号 :通过 WEBGL_debug_renderer_info 扩展获取真实的显卡型号
渲染管道差异 :不同 GPU 执行相同的着色器程序会产生细微差异
扩展支持 :不同的 GPU 支持不同的 WebGL 扩展
参数限制 :MAX_TEXTURE_SIZE、MAX_VIEWPORT_DIMS 等参数
实战代码 function getWebGLFingerprint ( ) {
const canvas = document .createElement ('canvas' );
const gl = canvas.getContext ('webgl' ) || canvas.getContext ('experimental-webgl' );
if (!gl) return null ;
const result = [];
result.push ('vendor:' + gl.getParameter (gl.VENDOR ));
result.push ('renderer:' + gl.getParameter (gl.RENDERER ));
result.push ('version:' + gl.getParameter (gl.VERSION ));
result.push ('shadingLanguageVersion:' + gl.getParameter (gl.SHADING_LANGUAGE_VERSION ));
const debugInfo = gl.getExtension ('WEBGL_debug_renderer_info' );
if (debugInfo) {
result.push ('unmaskedVendor:' + gl.getParameter (debugInfo.UNMASKED_VENDOR_WEBGL ));
result.push ('unmaskedRenderer:' + gl.getParameter (debugInfo.UNMASKED_RENDERER_WEBGL ));
}
result.push ('maxTextureSize:' + gl.getParameter (gl.MAX_TEXTURE_SIZE ));
result.push ('maxViewportDims:' + gl.getParameter (gl.MAX_VIEWPORT_DIMS ));
result.push ('maxVertexAttribs:' + gl.getParameter (gl.MAX_VERTEX_ATTRIBS ));
return result.join ('|' );
}
高级技术:WebGL 渲染指纹
function getAdvancedWebGLFingerprint ( ) {
const canvas = document .createElement ('canvas' );
const gl = canvas.getContext ('webgl' );
const vShader = gl.createShader (gl.VERTEX_SHADER );
gl.shaderSource (vShader, `attribute vec2 attrVertex; void main() { gl_Position = vec4(attrVertex, 0.0, 1.0); } ` );
gl.compileShader (vShader);
const fShader = gl.createShader (gl.FRAGMENT_SHADER );
gl.shaderSource (fShader, `precision mediump float; void main() { gl_FragColor = vec4(0.5, 0.5, 0.5, 1.0); } ` );
gl.compileShader (fShader);
const program = gl.createProgram ();
gl.attachShader (program, vShader);
gl.attachShader (program, fShader);
gl.linkProgram (program);
gl.useProgram (program);
const pixels = new Uint8Array (4 );
gl.readPixels (0 , 0 , 1 , 1 , gl.RGBA , gl.UNSIGNED_BYTE , pixels);
return pixels.join (',' );
}
为什么 WebGL 指纹如此强大? 1. 唯一性极高 :GPU 型号 + 驱动版本的组合几乎是唯一
2. 难以伪造 :除非使用虚拟机或模拟器,否则无法欺骗真实的 GPU
3. 跨会话稳定 :除非更换显卡或驱动,否则指纹基本不变
但有个致命弱点 :某些浏览器(如 Tor Browser)完全禁用 WebGL,或者某些隐私插件会拦截 WEBGL_debug_renderer_info 扩展。
Audio 指纹:声音里的身份 如果说 Canvas 和 WebGL 是"视觉指纹",那 Audio 指纹就是"听觉指纹"——通过音频处理管道的微小差异来识别设备。
原理剖析 Audio 指纹的原理是利用 AudioContext API:
创建一个离线的 AudioContext
生成一个特定的音频信号(通常是正弦波或压缩信号)
通过音频处理节点(如 DynamicsCompressorNode)
读取处理后的音频样本
不同设备的音频处理硬件和软件会导致微小的差异
采样率转换 :不同系统使用不同的重采样算法
浮点精度 :CPU 处理浮点运算的精度差异
音频驱动 :操作系统音频驱动层的实现差异
实战代码 async function getAudioFingerprint ( ) {
try {
const AudioContext = window .OfflineAudioContext || window .webkitOfflineAudioContext ;
if (!AudioContext ) return null ;
const context = new AudioContext (1 , 44100 , 44100 );
const oscillator = context.createOscillator ();
oscillator.type = 'triangle' ;
oscillator.frequency .setValueAtTime (10000 , 0 );
const compressor = context.createDynamicsCompressor ();
compressor.threshold .setValueAtTime (-50 , 0 );
compressor.knee .setValueAtTime (40 , 0 );
compressor.ratio .setValueAtTime (12 , 0 );
compressor.attack .setValueAtTime (0 , 0 );
compressor.release .setValueAtTime (0.25 , 0 );
oscillator.connect (compressor);
compressor.connect (context.destination );
oscillator.start (0 );
const renderedBuffer = await context.startRendering ();
const channelData = renderedBuffer.getChannelData (0 );
const samples = [];
for (let i = 4500 ; i < 5000 ; i += 10 ) {
samples.push (channelData[i].toFixed (10 ));
}
return hashString (samples.join (',' ));
} catch (e) {
return null ;
}
}
Audio 指纹的稳定性 Audio 指纹的优势在于它不太受软件版本影响 ,更多取决于硬件(声卡/音频芯片)。这意味着:
✅ 跨浏览器稳定 :Chrome 和 Firefox 在同一个设备上会产生相似的音频指纹
✅ 难以软件欺骗 :单纯的浏览器插件难以模拟硬件级音频特征
⚠️ 但不够唯一 :相比 Canvas 和 WebGL,Audio 指纹的区分度稍低,通常作为辅助指纹使用
其他指纹维度 除了三大核心指纹技术,还有很多"小而美"的指纹维度:
1. 字体指纹 function getFontFingerprint ( ) {
const baseFonts = ['Arial' , 'Times New Roman' , 'Courier New' ];
const testFonts = ['Helvetica' , 'Georgia' , 'Verdana' , 'Tahoma' ];
const detected = [];
const canvas = document .createElement ('canvas' );
const ctx = canvas.getContext ('2d' );
ctx.font = '72px ' + baseFonts[0 ];
const baselineWidth = ctx.measureText ('mmmmmmmmlli' ).width ;
testFonts.forEach (font => {
ctx.font = '72px "' + font + '", ' + baseFonts[0 ];
const width = ctx.measureText ('mmmmmmmmlli' ).width ;
if (width !== baselineWidth) {
detected.push (font);
}
});
return detected.join (',' );
}
2. 硬件信息 function getHardwareFingerprint ( ) {
return {
deviceMemory : navigator.deviceMemory ,
hardwareConcurrency : navigator.hardwareConcurrency ,
maxTouchPoints : navigator.maxTouchPoints ,
platform : navigator.platform ,
};
}
3. 时区和语言 function getTimezoneFingerprint ( ) {
return {
timezone : Intl .DateTimeFormat ().resolvedOptions ().timeZone ,
timezoneOffset : new Date ().getTimezoneOffset (),
languages : navigator.languages ,
language : navigator.language ,
};
}
反指纹技术:现代浏览器的防御 既然指纹技术如此强大,有没有办法防御呢?答案是——有,但不完美 。
1. Canvas Farbling(随机化噪声) 这是Brave 浏览器 首创的技术,后来被 Firefox 采用。
原理:在 Canvas 读取像素数据时,向某些像素注入微小的随机噪声(通常是 RGB 值的±1)。人眼无法察觉,但会破坏指纹哈希的稳定性。
const originalToDataURL = HTMLCanvasElement .prototype .toDataURL ;
HTMLCanvasElement .prototype .toDataURL = function (...args ) {
const data = originalToDataURL.apply (this , args);
return addFarblingNoise (data, getSessionSeed ());
};
检测 Farbling 的方法 (来自 fingerprintjs):
function detectCanvasFarbling ( ) {
const canvas = document .createElement ('canvas' );
const ctx = canvas.getContext ('2d' );
ctx.fillStyle = '#000' ;
ctx.fillRect (0 , 0 , 1 , 1 );
const data1 = canvas.toDataURL ();
const data2 = canvas.toDataURL ();
return data1 !== data2;
}
2. WebGL 扩展拦截 隐私插件(如 ScriptSafe)会拦截对 WEBGL_debug_renderer_info 的访问:
const originalGetExtension = WebGLRenderingContext .prototype .getExtension ;
WebGLRenderingContext .prototype .getExtension = function (name ) {
if (name === 'WEBGL_debug_renderer_info' ) {
return null ;
}
return originalGetExtension.call (this , name);
};
3. User Agent 标准化 现代浏览器开始减少 User Agent 的熵值:
4. Tor Browser 的极端策略 Tor Browser 采取了最激进的反指纹措施:
完全禁用 WebGL
统一所有用户的 User Agent(都显示为 Windows 7 + Firefox ESR)
标准化屏幕分辨率(仅报告几种常见尺寸)
禁用 Canvas 读取(或返回空白数据)
禁用所有时区检测(统一使用 UTC)
代价是:网站兼容性极差 ,很多现代 Web 应用无法在 Tor Browser 中正常运行。
实战:用开源库生成你的指纹
方案 1:FingerprintJS(最流行) npm install @fingerprintjs/fingerprintjs
import FingerprintJS from '@fingerprintjs/fingerprintjs' ;
async function getVisitorId ( ) {
const fp = await FingerprintJS .load ();
const result = await fp.get ();
console .log ('Visitor ID:' , result.visitorId );
console .log ('Components:' , result.components );
return result;
}
class BackendService {
async initDeviceID ( ) {
try {
const fp = await FingerprintJS .load ();
const result = await fp.get ();
this .deviceID = result.visitorId ;
} catch (error) {
console .error ('Fingerprint failed:' , error);
}
}
}
方案 2:GuardianJS(免费开源) npm install guardian-js-free
import { load } from 'guardian-js-free' ;
async function getGuardianFingerprint ( ) {
const guardian = await load ();
const visitorId = await guardian.getVisitorId ();
console .log ('Guardian ID:' , visitorId);
return visitorId;
}
方案 3:纯浏览器 API 实现 class BrowserFingerprinter {
async getFingerprint ( ) {
const components = await Promise .all ([
this .getCanvasFingerprint (),
this .getWebGLFingerprint (),
this .getAudioFingerprint (),
this .getFontFingerprint (),
this .getHardwareInfo (),
this .getTimezoneInfo (),
]);
const combined = components.join ('::' );
return this .hash (combined);
}
}
const fingerprinter = new BrowserFingerprinter ();
const id = await fingerprinter.getFingerprint ();
console .log ('Your fingerprint:' , id);
总结与思考
核心要点回顾
浏览器指纹利用了 Web 的开放性 :它不需要 Cookie,不违反任何协议,只是"读取浏览器本来就公开的信息"。
三大核心技术 :
Canvas 指纹 :2D 渲染差异,利用显卡驱动和字体渲染的不同
WebGL 指纹 :GPU 型号和渲染管道特征,几乎无法伪造
Audio 指纹 :音频处理差异,硬件级特征
2025 年的新趋势 :
学术研究首次实证指纹用于广告追踪
Google 政策转向,Privacy Sandbox 拥抱指纹技术
浏览器厂商加大反指纹力度(Farbling 成为标准)
给开发者的建议
优先考虑服务器端 Session + 登录态
如果需要客户端识别,可以使用 FingerprintJS 等成熟库
永远不要 将指纹用于违法追踪或侵犯隐私
教育用户使用 Brave、Firefox 等注重隐私的浏览器
安装 Privacy Badger、uBlock Origin 等扩展
对于高安全需求,考虑使用 Tor Browser
给普通用户的建议
不要迷信"无痕模式" :它只清除本地数据,无法阻止指纹追踪
安装隐私扩展 :uBlock Origin、Privacy Badger 能有效阻止大部分追踪
使用隐私浏览器 :Brave 的 Farbling 是目前最有效的反指纹手段
接受现实 :完全的匿名在当前 Web 技术下几乎不可能,除非你准备好牺牲便利性
参考链接 微信扫一扫,关注极客日志 微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
相关免费在线工具 加密/解密文本 使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online
Keycode 信息 查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online
Escape 与 Native 编解码 JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online
JavaScript / HTML 格式化 使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online
JavaScript 压缩与混淆 Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online
Base64 字符串编码/解码 将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online