跳到正文
Hacker News· royfish·· 6 天前AI 评分38

指纹识别正在取代 Cookies:Google 广告产品已支持设备指纹

Fingerprints Are the New Cookies

AI 导读

Google 广告产品现已支持设备指纹识别,用浏览器与硬件特征组合替代第三方 Cookies 追踪用户。指纹数据来自网络请求(IP、User-Agent、Accept-Language)与代码环境(GPU、CPU、内存、屏幕、音频处理、字体、扩展),组合后足以在样本中区分个体。Brave 用 farbling 注入噪声打乱指纹,Tor 与 Mullvad Browser 则让所有用户指纹保持一致。

正文 · AI 翻译

从现在起,我就叫你:

…

计算中…

试试刷新页面。清除 cookie。连上 VPN。

你仍然是那个 …。

现在用手机或其他设备打开它,你的昵称会不一样,但同样保持一致。

在无痕模式下不一样?很好。但它总是同一个?坏消息。

想象一下:

你访问的大多数网站靠展示广告赚钱,并间接运行我的脚本,让我知道你就是那个 …。我可以用它来决定你会看到什么广告。但更重要的是,如果你在那个网站上注册了账号,我可以提出购买你的数据——只要他们的隐私政策允许分享:你的姓名、活动、与 AI 的对话……

很快我就会比你的朋友更了解你——也许比你自己更了解你。

“那又怎样?”——你说。

“我没什么可隐藏的。而且我更喜欢相关的广告……有时候它们正好给我看我需要的东西!再说数据反正都是匿名的……”

你看,问题在于,这根本与你无关。也不再真的与广告有关了。没人在乎你。而如果警察或你的政府恰好在乎,我也很乐意收他们的钱。

我看着所有这些数据,看到的不是人。我看到的是模式。从模式中涌现出的模式。经济、社会和人类心理的一种活生生的、数字化的分形状态。21 世纪的水晶球,而它是硅做的!那才是产品。

然后它归出价最高者所有!

那这让你成了什么?一颗齿轮?一头牛?一个志愿者?


还记得关于第三方 cookie 的那场大吵大闹吗?闹得太大,以至于欧盟已经推出了它最伟大的发明之一——同意横幅。

如果欧盟比科技公司行动还快,那只能说明科技公司在袖手旁观。而其中一家科技公司恰好掌握着三分之二的浏览器市场。

这出戏是什么?

让我们为 Google 鼓掌——地球上最大的广告公司!

第一幕。施压——“隐私对我们至关重要,在我们所做的一切中都是如此。”

那是 2019 年,cookie 还很烫手。它已经成了负面公关,Safari 和 Firefox 已经屏蔽了它们。

一年后 Google 承诺:我们将消灭 cookie!!并建立一个全新的、隐私优先的统治。

第二幕。转移注意力——“与其弃用第三方 cookie,我们将引入一种全新体验”

5 年后:人们现在被远比 cookie 更大的忧虑占据,而 Google 开发的解决方案(Privacy Sandbox)有点烂,还引发了反垄断担忧,所以没人用它们。

“你知道吗,咱们就保留 cookie 吧。不过我们会掩盖你的 IP 地址*!”(* 但仅在已认证时使用无痕模式)(** 最终他们连这一点都没做到)

第三幕。加速——你现在可以在我们的广告产品中使用设备指纹识别

现在有了更好的办法来知道谁是谁!

指纹识别是怎么工作的?

好,咱们好好极客一把。

你访问一个网站。你的浏览器向服务器发送请求,服务器返回代码供浏览器运行。

这个请求已经暴露了关于你的信息:你用什么浏览器、它的语言、运行它的操作系统,以及你的 IP 地址——服务器需要它来给你发送响应。这通常会暴露你的位置。

然后代码开始运行。

你看,我们在让浏览更安全方面已经取得了许多文明层面的进步。仅仅因为访问一个网站就感染病毒的日子早已一去不复返了。问题是,安全的定义因问的人不同而不同。广泛的共识止步于隔离:一个网站的代码无法看到其他网站存储的数据,未经许可也无法看到你的文件或其他应用程序。Chrome 和 Edge 就到此为止。然后是 Safari(尤其是在 iOS/macOS 26+ 上)和 Firefox,它们将定义扩展到隐私,但仍不能完全保护你免受指纹识别。Brave 和 Tor 是领先的隐私优先浏览器,还有后起之秀 Mullvad Browser(在我看来,Mullvad 是市面上最好的 VPN 提供商之一)。

代码在你的硬件上运行,而硬件可以通过各种方式被测量,从而产生一致且独特的读数。对此其实没什么办法,所以只有两种成熟的解决方案:

  1. 到处掺入一点噪声。这叫做 farbling,Brave 就是这么做的,让你的指纹变得不一致。
  2. 让一切始终看起来都一样,这样就无法区分浏览器用户。Tor 和 Mullvad 采用这种方法。

好,让我们来创建一个指纹

所以,构成你指纹的数据有两个来源:网络请求和代码环境。

当你观察网络时,数据通常是脆弱或通用的:

  • IP 地址;在一个人始终使用同一个 IP 地址的“理想情况”下,这可能是你唯一需要的数据。但人们通常会在移动网络上大量浏览,与他人共享 WiFi,使用 VPN,或者他们家庭网络的 IP 地址被 ISP 轮换。因此它是脆弱的。
  • 像 User-Agent 或 Accept-Language 这样的标头;会暴露你的浏览器、操作系统、版本和语言设置。

当你能在设备上执行代码时,你就有办法测量其硬件特性(无需任何权限提示!),而这些特性出奇地稳定:

  • GPU;行为、厂商、驱动
  • CPU;行为、厂商、线程数
  • 内存大小
  • 显示数据;分辨率、像素比、刷新率
  • 音频处理
  • 设备型号

而在软件层面;已安装的字体、扩展程序、系统信息……

数百万台设备使用最新的 Chrome,有数百万台搭载 M4 芯片的 Mac,还有数百万个使用法语的浏览器。

但当你观察那些在 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 比特,即 1/2048。唉,正如 Thomas Bayes 会让你知道的那样,在实践中从来没有这么简单。胡子已经是男性的强烈指标,而男性更有可能只有一只眼睛。这些特征并不独立,所以比特数并不能真正相加。

这类推理的一些有趣应用:

  • 在医学诊断中:一种已知影响 0.01% 人口的疾病,其 99% 准确率的检测呈阳性结果,其错误的可能性大约是你真正患病的 100 倍。你需要更多证据!
  • 在密码学中:由四个随机单词组成的密码与八个随机键盘字符一样强(但更容易记住!)
    xkcd 936
  • 在法医学中:在一个百万人口的城市中锁定一个人大约需要 20 比特。一个与 1000 人中 1 人相符的目击者描述只提供 10 比特,这仍然留下了一千个可能的嫌疑人。

那我能做些什么呢?

测试你的浏览器:

如果你在使用 Chrome,你基本上就是额头上贴着身份证在到处走。

Safari 和 Firefox 提供了一定程度的保护,但你仍然可以被高级追踪器识别。

Brave、Tor 和 Mullvad Browser 是目前唯一实现了针对指纹识别的高级保护的主流浏览器。

如果你不想换浏览器,我理解。我自己是 Arc 的忠实粉丝,不幸的是它已停止开发,永远不会再有隐私功能了。有像 JShelter 这样的扩展——它们通过拦截浏览器 API 调用来干扰指纹识别脚本。但它们也会破坏一些网站。Firefox 的 Resist Fingerprinting 功能也有同样的问题。

讽刺的是,隐私扩展反而让你更独特,因为拥有它们的人更少。所以聪明的追踪器可以检测到这一点,忽略被扩展干扰的信号,专注于那些未被干扰的信号。

没有任何扩展能比浏览器内置的引擎级保护更有效。所以如果你不想被追踪,就必须下定决心。

本页使用的方法

我只是用你设备的几项测量值生成一个哈希。其中任何一项发生变化,哈希就会不同。专业应用比这复杂得多,并且能够在单个变量变化时保持识别线。

  1. 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);
    
  2. 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);
    
  3. canvas

    用几种字体、表情符号和渐变绘制文本,然后读回像素;抗锯齿和字体渲染取决于你的操作系统、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;
    
  4. Intl

    你的时区和日期及数字设置;VPN 会改变你的 IP 位置,但不会改变你的时区。

    const date = Intl.DateTimeFormat().resolvedOptions();
    // …
    return [date.timeZone, date.locale, date.calendar, date.numberingSystem, hourCycle, (1234567.891).toLocaleString()].join('/');
    
  5. navigator

    你的 CPU 核心数、内存大小(仅 Chrome)以及浏览器的语言。

    return [navigator.hardwareConcurrency, navigator.deviceMemory, navigator.language].join('/');
    
  6. crypto.subtle

    用 SHA-256 对所有值进行哈希,哈希为你的昵称挑选一个特征和一种动物。

    const digest = await crypto.subtle.digest('SHA-256', bytes);
    // …
    nickname: stranger || `${pick(TRAITS, hex, 0)} ${pick(ANIMALS, hex, 8)}`,
    

来源:Hacker News · royfish.dev