指纹是新的 cookie
第三方 cookie 没被淘汰,指纹识别先成了新的追踪手段。文章从一个算出访客昵称的演示讲起,拆开指纹的数据来源、信息熵如何把特征换算成比特,以及 Chrome、Safari、Tor 各自给出的答案。
中文
复制

你在这里不会被我用的这些手段识别。 你禁用了 JavaScript,所以这个指纹识别技巧根本跑不起来。但基于网络的手段对你依然有效。往下读,看看它对其他人做了什么。
从现在起,我就叫你:
… computing…
试试刷新页面。清掉 cookie。连上 VPN。
你依然是那个 …。
现在换手机或别的设备打开,你的昵称会不一样,但同样稳定。
无痕模式下不一样?很好。但永远都一样?那可不是好消息。
想象一下:
你访问的大多数网站靠展示广告赚钱,同时悄悄运行我的脚本,让我知道你就是那个 …。我可以用它决定给你看什么广告。但更重要的是,如果你在那个网站注册过账号,我可以提出购买你的数据——只要他们的隐私政策允许分享:你的姓名、活动记录、与 AI 的对话……
用不了多久,我对你的了解就会超过你的朋友——也许超过你自己。
“那又怎样?”——你说。
“我没什么可藏的。而且我更喜欢看相关的广告……有时候它们推的正是我需要的东西!再说数据反正都匿名化了……”
问题是,这根本与你无关。现在也早已不只是广告的事了。没人在乎你。但如果警察或政府恰好在乎,我也很乐意收他们的钱。
我看着这所有数据,看到的不是人。我看到的是模式。模式中浮现出模式。一个活生生的、数字化的分形——经济、社会和人类心理的状态。21 世纪的水晶球,而且是硅做的!这才是产品。
然后它被卖给出价最高的人!
那你算什么?一颗齿轮?一头牛?一个志愿者?
指纹是新的 cookie
还记得当年第三方 cookie 闹得多大吗?大到欧盟已经推出它最伟大的发明之一——同意横幅。
如果欧盟的推进速度比科技公司还快,那只能说明这些科技公司在袖手旁观。而其中一家科技公司,恰好握着三分之二的浏览器市场。
这出戏是怎么唱的?
给 Google 鼓个掌吧——地球上最大的广告公司!
第一幕:施压——“隐私对我们至关重要,贯穿我们做的每一件事。”
2019 年,cookie 还热乎着。但它已经成了公关负资产,Safari 和 Firefox 早就把它们拦了。
一年后 Google 承诺:我们要干掉 cookie!!并开创一个隐私优先的新王朝。
第二幕:转移视线——“与其弃用第三方 cookie,我们将推出一种全新体验”
5 年后:人们的注意力早已被远比 cookie 更让人头疼的事占据,而 Google 搞出来的方案(Privacy Sandbox)实在不怎么样,还引发了反垄断担忧,所以根本没人用。
“这样吧,cookie 就留着。不过我们会给你的 IP 地址打码!”( 仅限已登录状态下开启无痕模式时)(** 最后连这个也没兑现)
第三幕:加速——你现在可以在我们的广告产品中使用设备指纹了
现在有了更好的办法来认出谁是谁!
指纹识别是怎么工作的?
好,来正经地技术一把。
你访问一个网站。浏览器向服务器发送请求,服务器返回代码让浏览器执行。
请求本身就已经暴露了你的信息:你用什么浏览器、浏览器是什么语言、跑在什么操作系统上,以及你的 IP 地址——服务器需要它才能把响应发回给你。而 IP 往往就暴露了你的位置。
然后代码开始执行。
你看,我们在让浏览变得安全这件事上取得了不少文明层面的进步。仅仅访问一个网站就中病毒,那种日子早就过去了。问题在于,安全的定义因人而异。最广泛的共识止步于隔离:一个网站的代码看不到其他网站存储的数据,未经许可也看不到你的文件或其他应用。Chrome 和 Edge 就停在这里。再往前是 Safari(尤其是 iOS/macOS 26+ 上)和 Firefox,它们把安全的定义延伸到隐私,但仍然无法完全保护你免受指纹识别。Brave 和 Tor 是隐私优先浏览器里的领头羊,后起之秀还有 Mullvad Browser(Mullvad 在我看来是市面上最好的 VPN 服务商之一)。
代码跑在你的硬件上,而硬件是可以测量的,测出来的读数既一致又独特。这事其实没什么好办法,所以目前只有两种成熟方案:
- 到处掺一点噪声。这叫 farbling,Brave 就是这么做的,让你的指纹前后不一致。
- 让所有东西永远长得一模一样,这样就没法把浏览器用户区分开。Tor 和 Mullvad 走的是这条路。
好,来造一个指纹
能拼出指纹的数据有两个来源:网络请求和代码运行环境。
看网络这一层,数据要么脆弱,要么泛泛:
- IP 地址;在“理想情况”下,一个人始终用同一个 IP,那可能光靠它就够了。但现实中人们大量用移动网络上网,跟别人共用 WiFi,用 VPN,或者家里网络的 IP 被 ISP 轮换。所以它很脆弱。
User-Agent或Accept-Language这类头信息;会暴露你的浏览器、操作系统、版本号和语言设置。
当你能在设备上执行代码时,就有办法测量它的硬件特征(而且不需要任何权限弹窗!),这些特征稳定得惊人:
- GPU;行为、厂商、驱动
- CPU;行为、厂商、线程数
- 内存大小
- 显示数据;分辨率、像素比、刷新率
- 音频处理
- 设备型号
再到软件层面;已安装字体、扩展、系统信息……
几百万台设备跑着最新版 Chrome,几百万台 Mac 装着 M4 芯片,几百万个浏览器把语言设成法语。
但如果你只看在 M4 上用法语跑最新版 Chrome 的设备,这个数字就大幅下降了。再加上硬件测量数据,数量会低到实际上独一无二。
指纹不需要做到真正的全球唯一——只要在你的样本量里足够把人们区分开就行。
一个邮编(从 IP 推断)、一个出生日期(你在某个平台上自己交出去的)加上性别(你到哪儿都填),三者合起来就足以唯一识别超过 60% 的美国人。
你随时可以再加一个变量,把候选池进一步缩小。
信息熵
玩过 Akinator 吗?你心里想一个人物,灯神问你二十来个问题,然后就猜出来了。指纹识别本质上就是这么回事,只不过由你的浏览器替你回答问题。搞懂信息熵之后,Akinator 的魔法就只是一个简单的把戏。
用那个猜棋盘上人物的游戏来简化一下,问题类似“我想的人物是男的吗?”。如果我回答“是”,而你棋盘上一半的人物是男性,那你就只剩下一半候选。这时可以说,我的回答给了你 log₂(100 / 50) = log₂(2) = 1 比特的信息,因为它把候选池砍掉了一半。
一个回答带来的比特数叫作它的惊异度(surprisal):答案越罕见,它告诉你的越多。
- 每 2 个人中有 1 个具备的特征值 1 比特(是男性),
- 每 8 个中有 1 个值 3 比特(有胡子),
- 每 128 个中有 1 个值 7 比特(只有一只眼)。
惊异度衡量的是答案的价值,熵衡量的是问题的价值。如果 128 个人里有 1 个独眼,那么问这个问题,128 次里有 127 次会得到“否”。
| 回答 | 概率 | 惊异度 | 熵的贡献 |
|---|---|---|---|
| 是 | 1/128 | log₂(128 / 1) = 7 bits | 1/128 x 7 = ~0.055 |
| 否 | 127/128 | log₂(128 / 127) = ~0.0113 bits | 127/128 x 0.0113 = ~0.011 |
把每种可能回答的贡献加起来,这个问题本身约值 0.066 比特的熵——也就是你从一个回答中平均能获得的比特数。
当特征之间相互独立时,我们直接把比特数相加,或者把比例相乘,所以一个有胡子又独眼的男性意味着 11 比特,也就是 2048 分之 1。可惜,正如 Thomas Bayes 会让你明白的,现实中从来没有这么简单。胡子本身就是男性的强指示,而男性也更可能只有一只眼。这些特征并不独立,所以比特数并不能真正相加。
这类推理有一些有趣的应用:
-
医学诊断:一种已知影响 0.01% 人口的疾病,若用准确率 99% 的检测方法得到阳性结果,这个结果出错的概率大约是你真正患病的 100 倍。你需要更多证据!
-
密码学:由四个随机单词组成的密码,强度相当于八个随机键盘字符(但好记得多!)

-
法医学:在一座百万人口的城市里锁定一个人,大约需要 20 比特。一份只匹配千分之一人口的目击者描述,只提供 10 比特,仍然留下一千个可能的嫌疑人。
那我能做些什么?
测试你的浏览器:
- coveryourtracks.eff.org 会显示你的指纹有多独特,并给出每个测量数据点的熵值。
如果你在用 Chrome,基本上等于额头上贴着身份证在街上走。
Safari 和 Firefox 提供了一定程度的保护,但高级追踪器仍然能识别出你。
目前,Brave、Tor 和 Mullvad Browser 是仅有的几款实现了高级反指纹保护的主流浏览器。
如果你不想换浏览器,我理解。我自己就是 Arc 的忠实用户,可惜它已经停止开发,永远不会加入任何隐私功能。也有像 JShelter 这样的扩展——它们通过拦截浏览器 API 调用来干扰指纹脚本,但也会弄坏一些网站。Firefox 的 Resist Fingerprinting 功能也有同样的问题。
讽刺的是,隐私扩展反而让你更独特,因为用的人更少。聪明的追踪器可以据此识别出你,忽略被扩展干扰的信号,专注于没被干扰的那些。
任何扩展都比不上浏览器内置的引擎级保护。所以如果你不想被追踪,就得下决心换。
本页使用的方法
我只是对你的设备做几项测量,然后生成一个哈希。其中任何一项发生变化,哈希就会不同。专业应用比这复杂得多,能在单个变量变化时仍保持识别的一致性。
-
OfflineAudioContext用压缩器渲染一小段音调但不播放;结果中细微的浮点差异取决于浏览器的音频引擎和你的 CPU。const off = new OfflineAudioContext(1, 5000, 44100); // … a 1 kHz triangle wave into a dynamics compressor … const data = (await Promise.race([off.startRendering(), timeout])).getChannelData(0); let sum = 0; for (let i = 4500; i < 5000; i++) sum += Math.abs(data[i]); return sum.toFixed(3); -
WebGL读取你 GPU 的厂商和型号(不含版本号)、GPU 及其驱动支持的特性,以及一个小三角形渲染后的像素。const gl = document.createElement('canvas').getContext('webgl'); const dbg = gl.getExtension('WEBGL_debug_renderer_info'); return gpuName(gl.getParameter(dbg ? dbg.UNMASKED_RENDERER_WEBGL : gl.RENDERER) || ''); // "ANGLE (Apple, ANGLE Metal Renderer: Apple M4, Unspecified Version)" -> "apple m4" // … return fnv1a(new TextEncoder().encode([...(gl.getSupportedExtensions() || []).sort(), ...limits, ...precision].join('|'))); // … gl.drawArrays(gl.TRIANGLES, 0, 3); gl.readPixels(0, 0, canvas.width, canvas.height, gl.RGBA, gl.UNSIGNED_BYTE, px); -
canvas用几种字体、emoji 和渐变绘制文字,然后读回像素;抗锯齿和字体渲染取决于你的操作系统、GPU 和字体。ctx.font = '15px "Times New Roman", serif'; ctx.fillText('Sphinx of black quartz 🦊', 4, 22); // … const data = ctx.getImageData(0, 0, 240, 60).data; -
Intl你的时区以及日期和数字格式;VPN 会改变你的 IP 位置,但不会改变你的时区。const date = Intl.DateTimeFormat().resolvedOptions(); // … return [date.timeZone, date.locale, date.calendar, date.numberingSystem, hourCycle, (1234567.891).toLocaleString()].join('/'); -
navigator你的 CPU 核心数、内存大小(仅 Chrome)以及浏览器的语言。return [navigator.hardwareConcurrency, navigator.deviceMemory, navigator.language].join('/'); -
crypto.subtle用 SHA-256 对所有值做哈希,哈希结果决定你昵称中的一个特征和一种动物。const digest = await crypto.subtle.digest('SHA-256', bytes); // … nickname: stranger || `${pick(TRAITS, hex, 0)} ${pick(ANIMALS, hex, 8)}`,