浏览器指纹原理:Canvas/WebGL指纹、指纹熵与匿名集
人机之辨那一篇把「识别」拆成了三级台阶,其中最上面一层——执行环境本身是不是真的——只点到为止。这一篇把它挖到底,从一个最被误解的概念切入:浏览器指纹。
多数人对指纹的想象是「网站偷偷收集了我的一堆属性」。这个画面既对又浅。对,是因为指纹确实由屏幕尺寸、时区、字体、Canvas 渲染这些属性构成;浅,是因为它没回答两个真正的问题:这些看起来人畜无害的属性,凭什么能唯一锁定一个人? 以及——一个指纹「真不真」,到底由什么决定?
第一个问题的答案是信息论;第二个问题的答案,藏在浏览器看不见的地方。
一、指纹不是属性集合,是可识别信息的载体
Section titled “一、指纹不是属性集合,是可识别信息的载体”先破直觉。单看指纹里的任何一个属性,几乎都不敏感:你的时区(全国就几个)、你的屏幕分辨率(撞款无数)、你安装的字体列表(和多数人雷同)。每一项单独拿出来,都无法指认你。
但把它们组合起来,情况陡变。时区 + 分辨率 + 字体列表 + 语言 + Canvas 渲染结果……这一组属性的联合取值,在几十亿网民里,很可能只有你一个人是这个组合。
指纹的本质不是「属性」,而是「你在人群里有多特别」——它是一个关于可识别信息量的问题,而信息量的单位,是比特。
这就是为什么理解指纹必须从信息论开始:指纹的威力,不在于它记录了什么,而在于它携带了多少能把你从人群里分离出来的比特。
二、可识别性的信息论:熵、惊异与匿名集
Section titled “二、可识别性的信息论:熵、惊异与匿名集”把上面的直觉形式化。设在一个 人的总体里,指纹取值 的概率是 。整个指纹分布的熵衡量它平均能提供多少识别信息:
而对某一个具体指纹 ,它携带的可识别信息是它的自信息(surprisal):
指纹越罕见( 越小), 越大,越能锁定你。把它翻译成人话,就是匿名集(anonymity set):设有 个人和你共享同一个指纹,那么这个指纹提供的可识别信息是
当 (指纹在总体里唯一),它提供 比特——在十亿人的总体里,约 30 比特,足以把你从所有人里精确点名。
关键的一步,是看每个属性的边际贡献。加入一个新属性带来的额外识别信息,是它相对已有指纹的条件熵 。这解释了一个反直觉现象:
少数几个高熵属性——尤其是 Canvas / WebGL / Audio 这类渲染指纹——就足以把匿名集压到 1。 而很多属性彼此高度相关(比如 UA 里的系统版本和字体列表强相关),它们的条件熵很低、贡献冗余。这就是指纹的信息论骨架:可识别性有上界(),逼近它靠的不是属性数量,而是几个真正高熵、低冗余的维度。
三、指纹从哪来:确定性的成因链
Section titled “三、指纹从哪来:确定性的成因链”那么,Canvas、WebGL、Audio 这些高熵指纹,为什么是稳定可复现的(每次都一样,才能当身份)、又为什么因设备而异(不同才有区分度)?
答案是:它们都是确定性渲染的产物。让浏览器画一段文字、渲染一个 3D 图元、合成一段音频,输出的每一个像素、每一个采样值,都由底层这条链共同决定:
同一条链,永远渲染出同一个结果(所以稳定);不同的硬件与软件栈组合,渲染出可分辨的差异(所以有熵)。Canvas 的像素差异可以追溯到 GPU 的光栅化实现、子像素抗锯齿、字体回退链;WebGL 追溯到 GPU 架构与图形驱动;Audio 追溯到音频子系统的浮点实现。指纹的熵,不是随机噪声,而是「世界上有多少种不同的软硬件栈组合」的映射。
这引出本篇的枢纽认知:
四、真实性是一个全栈问题——指纹能不能「自洽到底」
Section titled “四、真实性是一个全栈问题——指纹能不能「自洽到底」”现在问那个更深的问题:给定一个指纹,怎么判断它真不真?
如果指纹只是底层栈的投影,那么「真实性」就不是浏览器层能回答的——它是一个跨层一致性问题。检测方于是越挖越深:从 JS 可见的指纹,向下探到系统特征(字体在系统层如何注册、有没有传感器、GPU 是硬件加速还是软件光栅),再探到硬件行为本身。
于是「指纹真实性」呈现出一条光谱,取决于指纹背后到底是什么。从检测方的分析视角看,这条光谱大致是:
- 真实硬件(如产业里常见的真机设备农场):指纹跑在真实的硅片上,天然自洽——因为它就是真硬件逐层产生的,Canvas 对得上 GPU、GPU 对得上驱动、字体对得上系统,每一层的回溯都指向同一台真设备。这是获得「无需伪造的真实指纹」最直接的路径,也因此是产业里被广泛采用的一种做法。
- 虚拟化与软件合成:越往软件方向走,跨层保持一致就越难。软件光栅渲染的 WebGL 特征、缺失或不完整的传感器、与所声称机型错配的字体栈——这些「层与层之间对不上」的缝隙,正是检测方向下探时的着力点。
这也正是为什么,理解指纹的人不会把它当成「浏览器层的一串哈希」,而当成「一台设备从硅片到 JS 的完整身份投影」。
五、跨层一致性:指纹的自洽检验
Section titled “五、跨层一致性:指纹的自洽检验”把上面落到具体的检验上。一个可信的指纹,必须让下面这些彼此独立采集、却指向同一硬件的信号全都自洽:
- 浏览器声明(UA、所报机型)↔ Canvas/WebGL(实际渲染出的 GPU 特征):声称是某机型,渲染却是另一块 GPU,矛盾。
- Canvas ↔ WebGL:Canvas 的像素特征指向 GPU-A,WebGL 参数却报告 GPU-B,矛盾。
- 系统声明 ↔ 能力:声称是移动设备,却报告零触摸点、无运动传感器,矛盾。
- 硬件规格自洽:声称高端机型,
hardwareConcurrency(CPU 核数)、deviceMemory(内存)却对不上该机型的标称,矛盾。 - 指纹 ↔ 网络/环境:设备指纹指向某地区机型,出口 IP 的地理、时区、语言却是另一地,矛盾(详见后续的网络指纹篇)。
每一条都是一个两处独立信号必须相等的等式。伪造者要同时满足所有等式,难度随维度指数上升——又是那把维度诅咒的双刃剑,锋刃朝向防御方。
六、群体的视角:自然合法群与孤立群
Section titled “六、群体的视角:自然合法群与孤立群”前面五节,都在盯着一个指纹自己:它的可识别性、它的成因、它内部是否自洽。但最高阶的防御,根本不孤立地看单个指纹——它把整个人群拿来分桶分群。
这里的关键事实是:真实世界的指纹不是均匀散布的,而是成团的。 因为能被量产、上市、卖出千万台的软硬件栈组合,是有限的。一款热销机型,对应一个确定的「GPU + 驱动 + 字体 + 系统」组合,于是千万台同款真机会在指纹空间里自然聚成一个致密的簇。这些簇不是谁设计的,是物理与市场现实自然涌现的——世界上真实存在的设备型号有多少种,指纹空间里就有多少个致密团。
于是防御方做一件事:对全体指纹做无监督聚类 / 密度估计,再问每一个待检指纹——
你落在一个致密的真实簇里(自然合法群,千万真机共享),还是落在一片没人待过的空白区(孤立群 / 孤立点)?
这是一次决定性的升维。因为一个指纹完全可以:
- 每一维边缘都合理(第二节的信息论层面挑不出错);
- 跨层纵向自洽(第四、五节的全栈回溯也对得上);
- 却落在一个现实中从未量产过的组合上——它的联合取值掉进了真实流形的低密度、甚至零密度区。
前两关都过了,第三关照样挂。因为「自洽」只保证这个组合不自相矛盾,却不保证它真实存在过。一台世界上从没造出来过的设备,也可以是内部自洽的——但它在人群里没有同类,是一个孤立点。
这条原理还反手推翻了一个常见的隐私直觉——「把属性随机化,让自己独一无二,不就没人能追踪了吗?」恰恰相反。 随机化制造的是一个高熵的、独一无二的新组合,它几乎必然落在真实簇之外——你把自己变成了最显眼的孤立点。真正能藏住的方向,是让自己落进一个又大又密的自然合法群,和千万真实用户长得一模一样(呼应第二节的匿名集直觉:安全来自「和更多人相同」,而非「与所有人不同」)。
由此还能看清一个更硬的道理:一个可信的指纹,其各维度取值必须来自真实世界真正共同出现过的组合,而不是各维分别「看起来合理」再拼装起来的。凭空拼装一个「每维都合理」的组合,几乎注定落在低密度区、成为孤立点;只有源自真实观测、被现实反复验证过共同出现的组合,才稳稳落在自然合法群的中心。边缘的合理是廉价的,联合的真实是昂贵的。
七、这对隐私与反爬意味着什么
Section titled “七、这对隐私与反爬意味着什么”把信息论落到实践:
指纹是识别工具,但它的可识别性有信息论上界。 一个指纹能提供的比特数,不会超过 。对隐私一方,对抗指纹的方向因此很清晰:不是去「隐藏」某个属性(往往办不到),而是设法增大自己的匿名集、降低指纹的熵——让自己看起来和尽可能多的人一样。这也解释了为什么主流浏览器的反指纹策略是「趋同化」(让所有用户的某些属性长得一样),而不是「随机化」(随机化反而制造高熵的独特值,更容易被认出)。
对识别一方,真正的护城河不是「采集更多属性」,而是「跨层一致性检验」。 属性可以逐个伪造,但要让浏览器、系统、驱动、硬件的投影一路自洽地回溯到同一块硅,成本极高。这与本部反复出现的主题一脉相承:边缘可以伪造,结构难以伪造。
浏览器指纹是一台设备从硅片到 JS 的完整身份投影:它的可识别性由信息论定界(熵、匿名集、 的上界),它的真实性由全栈的纵向一致性决定(能不能一路回溯到同一块真实硬件)。
这口井还能往两个方向继续挖。一个方向是网络与协议层——在 JS 指纹之外,TLS 握手、HTTP/2 帧本身也构成一层指纹,且必须与浏览器指纹跨层自洽。另一个方向是把人机之辨里的行为生物特征展开成完整的统计建模。而无论哪个方向,度量这些黑盒时都要先守住那道方法论的坎——如何不被自己的错觉欺骗。
浏览器指纹不是属性的拼凑,而是可识别信息的载体:单个属性不敏感,联合起来却能把你从十亿人里锁定。它的威力由信息论定界——熵、自信息、匿名集,可识别性上界是 ,而逼近它靠的是少数几个高熵、低冗余的维度(Canvas/WebGL/Audio)。这些指纹是确定性渲染的产物,是「GPU + 驱动 + 字体栈 + 系统」这条链的逐比特投影——因而指纹本质上是底层硬件栈的顶层投影。由此,「真实性」是一个全栈问题:一个指纹可信与否,取决于它能否纵向一致地回溯到同一块真实硅片;从真实硬件到软件合成构成一条真实性光谱,层与层之间的缝隙就是识别的着力点。而最高阶的防御还要再加一维——群体视角:真实指纹在特征空间里自然聚成致密的簇(自然合法群),一个内部再自洽、却落在无人共享空白区的指纹,仍是可疑的孤立点。可识别性有上界、真实性靠纵向跨层自洽、合法性靠落进自然涌现的真实聚类——这是理解浏览器指纹的三把尺。