旨在对抗指纹追踪的扩展程序如何令指纹追踪变得更容易了

  • 本文探讨了现有的隐私保护扩展程序的缺陷

【注】本文的作者是安全研究人士 Wladimir Palant,它探讨了现有的隐私保护扩展程序的缺陷。这是个重要的问题。如果您想到了更好的解决方案,欢迎联系我们。

您的浏览器中是否安装了隐私保护扩展程序?有很多这类扩展程序,每个安全厂商都在推广自己的产品。通常情况下,这些东西会提供一个名为 “反指纹追踪” 或 “指纹追踪保护” 的功能,理论上它应该使您在网络上的身份可识别性降低。而您不会注意到的是:这个功能几乎都是有缺陷的,有可能反而让指纹追踪变得更容易了。

我见过很多扩展错误地声称了这个功能,但我却很少去写报告。要充分解释这个问题有点费劲。另一方面,很明显,对于大多数厂商来说,隐私保护只是他们可以放在功能列表上的一个噱头。质量并不重要,因为没有用户能够判断他们的解决方案是否真的有效。在可用资源极少的情况下,我的问题报告不太可能引起有意义的行动。

这就是为什么我决定在一篇博文中解释这些问题,下一次我遇到浏览器扩展程序遭受所有相同的缺陷时,我可以把这篇文章的链接发给他们。也许有些厂商就会解决这些问题。或者,更好的是,一开始就不犯这些错误。

指纹追踪的工作原理

当您浏览网页时,您不仅仅是在与您所访问的网站互动,还在与众多第三方互动。其中许多人对在不同网站上可靠地识别您有巨大的兴趣,例如广告商希望 “个性化” 给您的广告。传统的方法是在您的浏览器中存储一个cookie,其中包含您的唯一标识符。然而,现代浏览器有一个非常值得推荐的设置,即 在浏览会话结束时清除cookie。还有一种私密浏览模式,在这种模式下,不会永久地存储任何Cookie。预计很快会对第三方cookie实施进一步的技术限制,欧盟的数据保护规则也使存储cookie变得复杂,至少可以说是如此。

所以 Cookie 越来越不靠谱了。指纹追踪应该是解决这个问题,通过识别个人用户,而不在用户端存储任何数据。它的想法是查看浏览器无论如何都会提供的有关用户系统的数据,例如显示分辨率。数据是什么并不重要,重要的是:

  • 足够稳定,最好几个月不变
  • 特征性强,一小部分人所独有的

请注意,没有任何数据点需要单独确定一个人。如果每一个数据点都指的是不同的人群,那么只要有足够多的数据点,所有这些人群的交叉点将永远是一个人。

反指纹追踪应该如何操作?

反指纹追踪的目标是减少可用于指纹识别的数据的数量和质量。例如,CSS曾经允许识别用户以前访问过的网站  — — 这是一个设计上的缺陷,可以用于指纹识别等。虽然花了不少时间和精力,但最终浏览器们还是找到了一个不会破坏网页的修复方法。如今,这个数据点已经不再对网站开放。

其他数据点依然存在,但已经被大大化解。例如,浏览器为网站提供了一个用户代理字符串,使这些网站知道例如他们正在处理的是哪个浏览器品牌和版本。用户安装的应用程序用自己的标识符来扩展这个用户代理字符串。最终,浏览器供应商认识到这可能被滥用于指纹识别,并决定删除任何第三方添加的信息。这里原来的许多其他信息也被删除了,所以今天任何用户代理字符串通常是一大群人的共同信息。

攻击错了目标

浏览器厂商已经在反指纹追踪方面投入了大量的工作。不过,他们通常只限于不会破坏现有网站的措施。而像显示分辨率(不像窗口大小)这样的东西并没有被太多的网站所考虑,但这些显然已经足够了,浏览器仍然会给他们提供用户的显示分辨率和可用空间(通常显示分辨率不包括任务栏)。

而隐私保护扩展则没有表现出那么多的关注。所以他们一般会做这样的事:

screen.width = 1280;
screen.height = 1024;

网站现在将看到每个人都是相同的显示分辨率对吗?好吧,除非网站这样做:

delete screen.width;
delete screen.height;

突然间 screen.width 和 screen.height 恢复到了原来的值。指纹追踪现在可以使用两个数据点而不是一个数据点:不仅仅是真实的显示器分辨率,还有假的。即使那个假的显示器分辨率极其普通,也会让指纹追踪稍微精确一些。

这是魔术吗?不是,只是 JavaScript 原型的工作原理。这些属性并不是定义在屏幕对象本身,而是对象原型的一部分。所以那个隐私扩展为原型的属性添加了一个重写。删除了重写后,原来的属性又变得可见了。

那么这样的做法正确吗?

Object.defineProperty(Screen.prototype, "width", {value: 1280});
Object.defineProperty(Screen.prototype, "height", {value: 1024});

好多了。网站不再能轻易地检索到原始值。但是,它可以通过调用 Object.getOwnPropertyDescriptor(Screen.prototype, “width”) 来检测该值是否被操纵。通常情况下,生成的属性描述符会包含一个getter,然而这个属性描述符有一个静态值。而事实上,一个隐私扩展程序在捣鼓这些值,又是一个可用的数据点。

让我们在不改变属性描述符的情况下尝试一下:

Object.defineProperty(Screen.prototype, "width", {get: () => 1280});
Object.defineProperty(Screen.prototype, "height", {get: () => 1024});

差不多了。但是现在网站可以调用 Object.getOwnPropertyDescriptor(Screen.prototype, “width”).get.toString() 来查看我们getter的源码。同样是一个数据点,可以用来做指纹识别。源码需要隐藏:

Object.defineProperty(Screen.prototype, "width", {get: (() => 1280).bind(null)});
Object.defineProperty(Screen.prototype, "height", {get: (() => 1024).bind(null)});

这个 bind() 调用确保 getter 看起来像一个本地函数。这正是我们所需要的。

Firefox 允许内容脚本调用 exportFunction(),这是一个更好的方法。尤其是,它不需要在网页上下文中注入任何代码。不幸的是,这个功能在基于 Chromium 的浏览器中是不可用的。

捕捉所有这些讨厌的框架

这里有一个复杂的问题:一个网站不仅仅有一个JavaScript执行上下文,它的每个框架都有一个。所以您必须确保您的内容脚本能在所有这些框架中运行。所以浏览器扩展通常会在其 manifest 中指定 “all_frames”: true。这是对的。但是,网站就会做这样的事:

var frame = document.createElement("iframe");
document.body.appendChild(frame);
console.log(screen.width, frame.contentWindow.screen.width);

为什么这个新创建的框架还在报告原来的显示宽度?我们又回到了原点:网站又有了两个数据点,而不是一个。

这里的问题是:如果没有设置框架位置,默认是加载特殊页面 about:blank。当Chrome开发者最初创建他们的扩展API时,他们没有给扩展提供任何方法来运行这里的内容脚本。幸运的是,现在这个漏洞已经被堵上了,但扩展清单也必须设置为 “match_about_blank”: true

时机问题

由于浏览器扩展中的防指纹追踪功能具有相当大的侵入性,容易破坏网站。所以让用户在特定的网站上禁用这个功能是很重要的。这就是为什么您会经常在扩展内容脚本中看到这样的代码:

chrome.runtime.sendMessage("AmIEnabled", function(enabled)
{
  if (enabled)
    init();
});

因此,这个内容脚本不是立即初始化所有的防指纹措施,而是先等待扩展的后台页面告诉它是否真的应该做什么。这给了网站必要的时间来存储所有相关的值,在它被更改之前。网站甚至可以稍后再来检查被更改后的值 — — 再次强调,两个数据点比一个好。

这是Chrome浏览器扩展架构的一个重要局限性,很遗憾,这也是当今所有浏览器所共有的。可以在任何网页脚本运行之前运行一个内容脚本 (“run_at”: “document_start”)。然而这将只是一个静态脚本,不知道任何扩展状态。而且请求扩展状态需要时间。

这可能最终会通过动态内容脚本支持得到解决,这是十年前就提出的要求。然而与此同时,似乎唯一可行的解决方案是立即初始化反指纹追踪。如果扩展后来说 “不,你被禁用了” — — 好吧,那么内容脚本就只能撤销所有的操作了。但这种方法可以确保在普通情况下(功能已启用的情况下)网站不会看到相同数据的两个变体。

伪造的艺术

比方说,所有的技术问题都解决了。安装假值的机制完美无缺。这还是有一个问题:如何选择 “合适” 的假值?

随机选择一个值呢?我的显示分辨率是1661×3351,指纹识别并不依赖于有意义的数据,它需要的是稳定且足够独特的数据。而这个显示分辨率无疑是极其独特的。现在人们可以想出方案来定期改变这个数值,但事实是:让用户脱颖而出并不是正确的方式。

您更希望的是找到一个最大的群体,然后加入其中。我的显示分辨率是1920×1080 — — 就是常见的全高清,没啥好看的。想知道我的可用显示空间吗?我的Windows任务栏在底部,和大家一样。不,我也没有调整它的大小。我只是普通人。

这种方法唯一的麻烦是:必须定期重新评估数值。20年前,1024×768是最常见的显示分辨率,也是防指纹追踪的好选择。而今天,如果有人宣称自己的屏幕尺寸是这样的,肯定会引人注目。同样,在网站日志中,声称使用Firefox 48的访问者也会很突出:几年前,它可能是一个常见的浏览器版本,但今天通常是仅仅假装成网站访问者的机器人。⚪️

How anti-fingerprinting extensions tend to make fingerprinting easier

发表评论

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据