侧边栏壁纸
博主头像
踏浪而行生活圈

行动起来,活在当下

  • 累计撰写 21 篇文章
  • 累计创建 7 个标签
  • 累计收到 0 条评论

目 录CONTENT

文章目录

未登录也能知道你是谁?浏览器指纹了解一下!

前段时间我在逛某个电商网站,看了一双鞋,没加购物车也没登录。结果切到另一个完全不相关的网站,赫然发现那双鞋的广告就挂在侧边栏。

换以前我可能会说"Cookie 追踪嘛,老套路了"。但那天我特意清了所有 Cookie,开了无痕窗口,甚至换了浏览器——广告还是跟过来了。

这让我开始认真琢磨一个问题:网站到底用什么方式在无痕模式下还能认出我?

答案就是今天的主角——浏览器指纹(Browser Fingerprint)

浏览器指纹概念图

什么是浏览器指纹?

简单来说,你的浏览器在访问网站时,会主动或被动地暴露一大堆信息:屏幕分辨率、安装了哪些字体、显卡型号、操作系统版本、时区、语言偏好……这些信息单独拿出来都很普通,但组合在一起,就形成了一份几乎独一无二的"身份证"。

就像人的指纹一样——每个人的指纹纹路都不同。浏览器指纹也是同样的道理。

有研究表明,浏览器指纹的唯一识别率可以达到 90% 以上。换句话说,十个用户里有九个,光靠这些"公开信息"就能精准锁定。

说实话,第一次知道这个数据的时候我后背有点凉。

指纹是怎么"采集"的?

你可能好奇,网站到底通过什么手段来采集这些信息的?下面我按"隐蔽程度"从低到高,逐个聊聊。

1. 基础信息采集:User-Agent 等

这是最浅的一层,也是大家最熟悉的。你打开浏览器控制台输入:

console.log(navigator.userAgent);

屏幕上会返回一串类似这样的字符串:

Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36

这一行就包含了:操作系统、CPU 架构、浏览器名称和版本。

除此之外,navigatorscreen 对象还暴露了更多信息:

// 屏幕分辨率
console.log(screen.width, screen.height, screen.colorDepth);

// 语言和时区
console.log(navigator.languages);
console.log(Intl.DateTimeFormat().resolvedOptions().timeZone);

// 硬件并发数(CPU 核心数)
console.log(navigator.hardwareConcurrency);

// 设备内存(部分浏览器)
console.log(navigator.deviceMemory);

// 是否支持触摸
console.log(navigator.maxTouchPoints);

这些数据单独看没啥,但组合起来就很有辨识度了。比如说——一台 16 寸 MacBook Pro、16GB 内存、8 核 CPU、时区 Asia/Shanghai、Chrome 122 版本的用户,你觉得有多少?

2. Canvas 指纹:用你的显卡"画画"

这个就比较有意思了。

原理是这样的:网站在页面上偷偷创建一个 <canvas> 元素,画一些文字和图形,然后通过 toDataURL() 把画布内容导出为图片数据。

关键在于:不同的操作系统、不同的显卡驱动、不同的浏览器,在渲染同一段文字和图形时,输出的像素会有微妙差异。

const canvas = document.createElement('canvas');
canvas.width = 200;
canvas.height = 50;
const ctx = canvas.getContext('2d');

// 画一段文字
ctx.textBaseline = 'top';
ctx.font = '14px Arial';
ctx.fillStyle = '#f60';
ctx.fillRect(125, 1, 62, 20);
ctx.fillStyle = '#069';
ctx.fillText('Browser Fingerprint', 2, 15);

// 导出像素数据
const dataURL = canvas.toDataURL();
console.log(dataURL.substring(22, 32)); // 取一段哈希

我记得第一次在自己电脑上跑这段代码的时候,盯着控制台那串乱码一样的 base64 数据看了好久,心想"就这?"——然后换了台电脑再跑一遍,得到的值果然不一样。那种感觉还挺魔幻的。

Canvas 指纹原理

3. WebGL 指纹:3D 渲染的"指纹"

Canvas 指纹的进阶版。原理类似,但这次用的是 WebGL 来渲染 3D 图形,采集的信息更丰富:

const canvas = document.createElement('canvas');
const gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl');

// 获取渲染器信息
const debugInfo = gl.getExtension('WEBGL_debug_renderer_info');
console.log(gl.getParameter(debugInfo.UNMASKED_VENDOR_WEBGL));    // 显卡厂商
console.log(gl.getParameter(debugInfo.UNMASKED_RENDERER_WEBGL));  // 显卡型号

// 获取支持的扩展列表
const extensions = gl.getSupportedExtensions();
console.log(extensions);

不同显卡(NVIDIA、AMD、Intel 集显)在 WebGL 渲染时的精度、抗锯齿方式都不一样,这就给了网站一个相当稳定的指纹来源。

4. 音频指纹:听你"说话"

这个就更隐蔽了。网站利用 Web Audio API 生成一段音频信号,经过一系列处理后分析输出结果:

const audioCtx = new (window.AudioContext || window.webkitAudioContext)();
const oscillator = audioCtx.createOscillator();
const analyser = audioCtx.createAnalyser();
const gain = audioCtx.createGain();
const scriptProcessor = audioCtx.createScriptProcessor(4096, 1, 1);

oscillator.type = 'triangle';
oscillator.frequency.setValueAtTime(10000, audioCtx.currentTime);
gain.gain.setValueAtTime(0, audioCtx.currentTime);

oscillator.connect(analyser);
analyser.connect(scriptProcessor);
scriptProcessor.connect(gain);
gain.connect(audioCtx.destination);

oscillator.start(0);

scriptProcessor.onaudioprocess = function(event) {
  const data = new Float32Array(analyser.frequencyBinCount);
  analyser.getFloatFrequencyData(data);
  // 对 data 做哈希处理,即为音频指纹
  console.log(data.slice(0, 30));
};

不同设备的音频处理芯片、音频驱动差异会导致输出信号有微小不同。老实说,这个方法我第一次见到的时候觉得挺"暴力美学"的——用声音来识别硬件,有点像谍战片里通过打字声锁定嫌疑人。

5. 字体枚举:看你装了什么"字体"

不同的操作系统和用户习惯会导致安装的字体列表大不相同。网站可以通过 CSS 来探测:

// 基础字体(几乎所有系统都有)
const baseFonts = ['monospace', 'sans-serif', 'serif'];

// 待检测的字体
const testFonts = [
  'Arial', 'Helvetica', 'Times New Roman', 'Courier New',
  'Verdana', 'Georgia', 'Palatino', 'Garamond',
  'Comic Sans MS', 'Impact', 'Lucida Console', 'Tahoma'
];

function isFontAvailable(font) {
  const span = document.createElement('span');
  span.style.position = 'absolute';
  span.style.left = '-9999px';
  span.style.fontSize = '72px';
  span.style.fontFamily = font;
  span.textContent = 'mmmmmmmmmmlli';
  document.body.appendChild(span);
  
  const defaultWidth = span.offsetWidth; // 用基础字体的宽度做对比
  // ... 对比逻辑
}

举个例子,如果你装了「思源黑体」、「霞鹜文楷」这种不太常见的字体,那你的指纹就更加独特了。

我自己的 Mac 上因为做设计的原因装了一堆奇奇怪怪的字体,后来拿工具一测,好家伙,字体指纹在全网用户中几乎是唯一的。

这些数据怎么变成"指纹"的?

你可能会问:这些零散的数据,怎么变成一个可追踪的标识?

过程其实很简单:

原始数据 → 拼接成字符串 → 计算哈希值(如 MurmurHash)→ 得到唯一的指纹 ID

比如把采集到的数据拼成这样:

Chrome|122.0.0.0|MacIntel|1680x1050|24|Asia/Shanghai|zh-CN,en|8|16|Canvas: a1b2c3...|WebGL: d4e5f6...

然后对这个字符串做一次哈希,得到一个类似 e4d7f1b4e2a7 的短字符串。这个字符串就是你的"指纹"——不依赖任何存储,清不掉、关不掉

FingerprintJS:这个领域的"瑞士军刀"

聊浏览器指纹就绕不开 FingerprintJS 这个开源项目。

它是目前最流行的浏览器指纹库,开源版本在 GitHub 上有 2 万+ Star,采用 MPL-2.0 协议。商业版本号称识别准确率能达到 99.5%

基本用法很简单:

<script src="https://openfpcdn.io/fingerprintjs/v4/iife.min.js"></script>
<script>
  const fpPromise = FingerprintJS.load();

  fpPromise.then(fp => fp.get()).then(result => {
    const visitorId = result.visitorId;
    console.log('你的指纹 ID:', visitorId);
    console.log('置信度:', result.confidence);
  });
</script>

就这几行代码,网站就能给当前访客生成一个稳定的标识符。

我自己在项目里试过 FingerprintJS 的开源版。说实话,在同一台设备上反复测试,visitorId 确实非常稳定——清 Cookie 没用,换无痕窗口没用,甚至升级浏览器小版本号都没变。

代码与隐私


那些"反指纹"的招数,好使吗?

知道了这些之后,第一反应肯定是:我能防吗?

坦率地说,很难。但也不是完全没办法。

浏览器隐私模式

无痕模式不能防止浏览器指纹采集。它只是不保存历史记录和 Cookie,但指纹数据是你浏览器"主动暴露"的,跟存储无关。

隐私浏览器(Brave、Tor)

  • Brave:内置了指纹防护,会对 Canvas、WebGL 等 API 的返回值做随机化处理。效果还不错,但可能会影响一些依赖这些 API 的正常网站。
  • Tor Browser:所有用户使用相同配置,强制统一屏幕大小、字体、插件等,让所有人的指纹趋同。思路很聪明——不是隐藏指纹,而是让所有人"长得一样"。

浏览器扩展

有一些扩展(如 CanvasBlocker、Privacy Badger)可以拦截或随机化指纹数据。但要注意,安装了隐私扩展本身也会成为指纹的一部分——想想看,一个装了 CanvasBlocker 的 Chrome,和一个裸奔的 Chrome,在指纹上是不是不同?

这就像你想伪装成普通人走在街上,结果戴了个超大的墨镜和口罩——反而更显眼了。

Chrome 的隐私沙盒(Privacy Sandbox)

Google 在推进的 Privacy Sandbox 计划(Topics API 替代第三方 Cookie)主要是解决广告追踪问题,但对浏览器指纹本身的防护很有限。说白了,这更多是 Google 在商业利益和隐私之间的平衡术。


那网站用指纹追踪我,合法吗?

这是一个好问题,也是我个人比较纠结的地方。

技术角度来说,浏览器指纹不存储任何数据在用户设备上,所以它不违反欧盟 GDPR 中关于 Cookie 的规定(ePrivacy Directive)。但 GDPR 本身对"个人数据"的定义非常宽泛——如果一个指纹能持续追踪某个用户,那它在法律意义上可能就被视为"个人数据"了。

目前各国监管还处于比较模糊的阶段。欧盟的 ePrivacy Regulation 一直在讨论中,还没最终落地。不过可以预见,随着公众隐私意识的提升,未来对浏览器指纹的监管一定会越来越严格。

我个人的立场是:技术本身无罪,但用来做什么很重要。 用指纹做反欺诈、风控——可以理解。用指纹做跨站追踪、精准广告轰炸——这就过分了。


写在最后

浏览器指纹让我重新审视了一件事:隐私不是你以为的样子。

我们花了很大力气去管理 Cookie、清理浏览记录、开无痕模式,但底层的技术手段远比我们想象的要丰富和隐蔽。你的浏览器,就像一个话痨,走到哪儿都把自己的"身份证"挂在嘴边。

作为开发者,了解这些技术是必要的——它能帮我们做出更好的安全决策。作为普通用户,至少要知道:无痕模式 ≠ 隐身模式。

如果你想亲自体验一下自己的浏览器指纹有多"独特",推荐去以下几个网站看看:

  • AmIUnique — 学术研究项目,能详细展示你的指纹组成
  • BrowserLeaks — 全面检测你的浏览器泄露了哪些信息
  • Cover Your Tracks — EFF(电子前哨基金会)出品

参考与致谢

本文在写作过程中参考了以下资源,在此一并感谢:

🙏 特别感谢 FingerprintJS 团队的开源贡献,该项目为浏览器指纹技术的研究和普及做出了巨大贡献。GitHub 地址:https://github.com/nicedoc/fingerprintjs

如果这篇文章对你有帮助,欢迎分享给身边的朋友。毕竟,了解一下自己在网上的"样子",总不是坏事。


往期精彩

小米深夜放了个"核弹":三大自研 MiMo-V2模型,这次真要"为发烧而生"

腾讯推出QClaw:微信远程操控电脑,本地AI助手开启内测

【好玩儿的Docker项目】用MuMuAiNovel自动写小说,docker一键部署,月入过万不是梦

欢迎大家关注我的同名公众号踏浪而行生活圈

0

评论区