指纹识别是指尝试在用户返回您的网站时识别用户,或者在不同的网站上识别同一用户。您的设置与其他人设置之间的许多特征可能有所不同。例如,您可能正在使用不同类型的设备和不同的浏览器,拥有不同的屏幕尺寸,并且安装了不同的字体。如果我安装了“Dejavu Sans”字体而您没有,那么任何网站都可以通过检查该字体来区分您和我。这就是指纹识别的工作原理;您建立这些数据点的集合,每个数据点都提供了更多区分用户的方法。
更正式的定义可能是这样的:指纹识别是使用用户设置的明显和不明显的长期特征来尝试将他们与尽可能多的其他用户区分开来的行为。
为什么指纹识别会阻碍用户隐私
在某些极端情况下,指纹识别用户非常重要:例如,欺诈检测。但指纹识别也可用于跨站点跟踪用户,而这种跟踪通常是在用户未同意的情况下完成的,或者在某些情况下,是基于未充分告知用户的无效同意。当这种情况发生时,这些用户通常会感到有些不安,并且感觉相当被背叛。
指纹识别是指找到秘密区分用户的方法。指纹识别可用于识别在同一网站上仍然是同一用户,或同时在两个不同的浏览器配置文件中识别同一用户。这意味着指纹识别可用于跨站点跟踪用户。确定性和公开的跟踪方法(例如,存储带有唯一用户特定 ID 的 Cookie)可以在一定程度上被用户观察和控制(之前的模块解释了其中一些方法)。但指纹识别更难避免,正是因为它具有隐蔽性;它依赖于不变的特征,并且很可能以隐形方式发生。这就是为什么它被称为“指纹识别”。无论是指纹识别还是指尖上的指纹,都很难改变。
浏览器供应商知道用户不喜欢被跟踪,并且不断实施限制指纹识别的功能(我们在之前的模块中看到了一些)。在这里,我们正在研究这些功能如何影响您的业务需求,以及如何在保护隐私的方式下仍然完成您想做的事情。这更多是关于浏览器防止指纹识别的保护措施将如何影响您所做的事情以及如何做,而不是关于它将如何阻止您进行指纹识别。
实际上,大多数开发者和大多数企业都不需要指纹识别用户。如果您的应用要求用户登录,那么您的用户正在向您表明身份,他们已同意,并且他们可以随时单方面选择退出。这是一种保护隐私的方法,可以了解哪些用户已登录。您的应用可能根本不需要用户登录,这更能保护用户的隐私(然后您只收集您所需的数据)。
执行
评估您的第三方是否存在指纹识别。作为第三方模块的一部分,您可能已经有了您包含的任何第三方服务以及它们发出的 Web 请求的列表。可以检查这些请求,看看是否将任何数据传递回发起者。但是,这通常很困难;指纹识别本质上是一个隐蔽的过程,涉及请求无需用户批准的数据。
还值得阅读您的第三方服务和依赖项的隐私政策,以查找使用指纹识别的迹象。有时它被称为“概率匹配”,或作为概率匹配技术套件的一部分,而不是“确定性匹配”。
指纹识别的工作原理
通常,您的所有这些属性的个人组合对您来说是独一无二的,或者至少对一小群相似的人来说是独一无二的;这可以用来秘密地跟踪您。
旁注:被动和主动指纹识别
这里有一个有用的区分,即被动指纹识别技术和主动指纹识别技术。被动指纹识别技术是默认情况下将信息提供给网站的技术;主动指纹识别技术是明确询问浏览器以获取额外信息的技术。这种区分很重要的原因是,浏览器可以尝试检测和拦截或减轻主动技术。API 可以受到限制,或通过对话框进行网关,询问用户的权限(因此提醒用户他们正在被使用,或允许用户默认拒绝它们)。被动技术是使用已经提供给网站的数据的技术,通常是因为历史上,在信息高速公路的初期,该信息被提供给所有网站。用户代理字符串就是这方面的一个例子,我们将在后面更详细地介绍这一点。它被认为有助于提供有关用户的浏览器、版本和操作系统的相当多的信息,以便网站可以选择根据该信息呈现不同的内容。然而,这也增加了可用的区分信息的数量,这些信息有助于区分用户,因此此类信息越来越不可用,或者至少被冻结,使其不再具有区分性。如果您的工作依赖于此信息(例如,如果您根据用户代理采用不同的代码分支),那么随着浏览器越来越多地冻结或停止该信息,此代码可能会中断。测试是此处最好的防御措施(请参阅下文)。
旁注:衡量指纹识别性
每个数据点提供的技术信息量度称为熵,以位为单位衡量。具有许多不同可能值的功能(例如,已安装字体的列表)可以为总数贡献很多位,而区分能力不强的功能(例如,您正在使用的操作系统)可能只会增加几位。HTTP Almanac 描述了现有的指纹识别库如何自动化将来自许多不同 API 的响应组合成“哈希”的过程,该哈希可能仅识别一小群用户,甚至可能仅识别一个用户。Maud Nalpas 在 此 YouTube 视频中详细介绍了这一点,但简而言之,想象一下您看到一个朋友列表,其中包含他们最喜欢的音乐、最喜欢的食物和他们说的语言……但他们的名字被删除了。任何一个人的列表都很可能在您的朋友中唯一地识别出他们,或者至少将列表缩小到只有几个人。这就是指纹识别的工作原理;您喜欢事物的列表变成了“哈希”。有了该哈希,在两个不同的不相关网站上识别用户是否为同一个人变得更容易,这就是跟踪的目的:规避用户对隐私的渴望。
浏览器如何对抗指纹识别?
重要的是,浏览器供应商非常清楚网站(或网站上包含的第三方)计算用户的区分性指纹的许多不同方法,或者不同的信息位如何有助于该指纹的唯一性。其中一些方法是明确和故意的,例如浏览器的用户代理字符串,它通常标识正在使用的浏览器、操作系统和版本(因此,如果您和我正在使用不同的浏览器,则有助于将您和我区分开来)。有些方法并非有意创建为可指纹识别的,但最终还是如此,例如字体列表或浏览器可用的视频和音频设备。(浏览器不必使用这些设备,只需按名称获取它们的列表。)有些方法在发布后很久才被确立为指纹识别的贡献者,例如 画布元素上字体抗锯齿的确切像素渲染。还有更多,您浏览器与我的浏览器不同的每种方式都会增加熵,因此可能会有助于区分您和我,并在网站上尽可能唯一地识别个人。https://amiunique.org 有一个很长(但绝对不全面)的潜在指纹识别贡献功能列表,并且该列表一直在增长(因为能够指纹识别用户有巨大的经济利益,即使这些用户不想要或可能不期望这样做)。
不支持某些强大的 API
浏览器供应商对所有这些计算用户指纹的方法的响应是找到减少这些 API 可用熵量的方法。最严格的选择是根本不实现它们。一些主要的浏览器已经针对各种硬件和设备 API(例如,来自客户端 Web 应用的 NFC 和蓝牙访问)完成了此操作,其中指纹识别和隐私问题被认为是它们尚未实现的原因。显然,这会影响您的应用和服务:您根本无法在不实现 API 的浏览器中使用 API,这可能会限制或完全切断某些硬件方法的考虑。
用户权限网关
浏览器供应商采取的第二种方法是在没有某种明确的用户权限的情况下阻止 API 或数据访问。这种方法通常也出于安全原因而被采用——网站不应该能够在未经您许可的情况下使用您的网络摄像头拍照!但在这里,隐私和安全可能具有相似的利益。识别某人的位置本身就是侵犯隐私的行为,当然,但它也是指纹唯一性的贡献者。要求获得查看地理位置的权限并不会减少位置添加到该指纹唯一性的额外熵,但它基本上消除了使用地理位置进行指纹识别,因为它不再是隐形完成的。指纹识别的重点是秘密地将用户彼此区分开来。如果您准备好让用户知道您正在尝试识别他们,那么您不需要指纹识别技术:让用户创建一个帐户并使用该帐户登录。
增加不可预测性
在某些情况下采取的第三种方法是浏览器供应商“模糊化”来自 API 的响应,以使其不那么精细,从而减少识别性。这在 数据模块中被描述为随机响应机制的一部分,当您从用户那里收集数据时,您可以这样做以避免无意中收集具有识别性的数据。浏览器供应商也可以对 Web 应用和第三方提供的 API 数据采取这种方法。这方面的一个例子是 非常精确的计时 API,用于测量页面性能,来自 window.performance.now()
。浏览器知道这些值精确到微秒,但返回的值会故意降低精度,方法是四舍五入到最接近的 20 微秒边界,以避免将其用于指纹识别(并且也为了安全起见,以避免计时攻击,这是公认的)。这里的目标是确保 API 保持有用,但使响应的识别性降低:本质上,通过使您的设备看起来更像其他人的设备而不是您独有的设备来提供“群体免疫”。Safari 出于此目的提供了系统配置的简化版本。
强制执行隐私预算
隐私预算是一个建议,建议浏览器估计每个指纹识别表面泄露的信息。它尚未在浏览器中实现。目标是在保留用户隐私的同时允许使用强大的 API。了解有关隐私预算提案的更多信息。
使用广泛的测试环境
所有这些都将影响您构建应用和服务的方式。特别是,在浏览器和平台之间,此领域的响应和方法存在广泛的多样性。这意味着在多种不同环境中测试您的工作至关重要。当然,这始终很重要,但可以合理地假设 HTML 呈现或 CSS 对于给定的渲染引擎是恒定的,无论该引擎位于哪个浏览器或平台中(因此,例如,可能很想只在一个基于 Blink 的浏览器中进行测试)。对于 API 使用而言,情况绝非如此,正是因为共享渲染引擎的浏览器在如何加强其 API 表面以防止指纹识别方面可能存在显着差异。
执行
- 检查您自己的分析和受众,以指导您在测试时应优先考虑的浏览器集。
- 要测试的一组好的浏览器是桌面上的 Firefox、Chrome、Edge、Safari、Android 上的 Chrome 和 Samsung Internet 以及 iOS 上的 Safari。这确保您跨三个主要的渲染引擎(Firefox 中的 Gecko、Chrome、Edge 和 Samsung Internet 中 Blink 的各种分支以及 Safari 中的 Webkit)以及移动和桌面平台进行测试。
- 如果您的网站也可能在不太常见的设备(例如平板电脑、智能手表或游戏机)上使用,请也在那里进行测试。某些硬件平台在浏览器更新方面可能会落后于移动设备和桌面设备,这意味着某些 API 可能在这些平台上的浏览器中未实现或不可用。
- 使用一个或多个声明用户隐私是其动机的浏览器进行测试。包括您最常用的浏览器的即将推出的预发布版本和测试版本(如果它们对您可用):Safari 的 技术预览版、Chrome 的 Canary 版、Firefox 的 Beta 频道。这些为您提供了在这些更改影响您的用户之前识别影响您网站的 API 中断和更改的最佳机会。同样,请参考您拥有的任何分析,牢记用户的环境。如果您的用户群中有大量旧款 Android 手机,请务必将这些手机纳入您的测试。大多数人没有开发团队拥有的快速硬件和最新版本。
- 使用干净的配置文件和隐身/隐私浏览模式进行测试;您很可能已经授予了个人配置文件中所需的权限。测试如果您拒绝网站的任何问题权限会发生什么情况。
- 在 Firefox 的 指纹识别保护模式下显式测试您的页面。这样做会在您的页面尝试指纹识别时显示权限对话框,或者会为某些 API 返回模糊数据。这有助于您确认您的服务中包含的第三方是否正在使用可指纹识别的数据,或者您自己的服务是否依赖于该数据。然后,您可以考虑有意的模糊化是否会使您更难做您需要做的事情。考虑进行相应的更正,以从另一个来源获取该数据、放弃该数据或使用不太精细的数据。
- 正如在 第三方模块中先前讨论的那样,审核您的第三方依赖项以查看它们是否正在使用指纹识别技术也很重要。被动指纹识别很难检测(如果第三方在其服务器上进行指纹识别,则不可能检测到),但指纹识别模式可能会标记某些指纹识别技术,并且查找 navigator.userAgent 的使用或意外创建
<canvas>
对象也可能会揭示一些值得进一步审查的方法。还值得在描述第三方的营销或技术材料中查找术语“概率匹配”的使用;这有时可能表明使用了指纹识别技术。
跨浏览器测试工具
出于隐私目的测试您的代码很难自动化,并且前面描述了手动测试时要查找的内容。例如,如果您拒绝网站访问其尝试访问的任何 API 的权限,会发生什么情况,以及如何向用户呈现这种情况?自动化测试无法判断网站的行为是否有助于用户信任它,或者相反地鼓励用户不信任它,或者认为某些东西被隐藏了。
但是,一旦站点经过审核,就可以自动化 API 测试以确认在较新的浏览器版本(或即将推出的“beta”和“预览”版本)中没有出现任何中断,并且在很大程度上应该作为您现有测试套件的一部分。在使用 API 表面覆盖率时,您的自动化测试工具需要考虑的一件事是,大多数浏览器都允许在一定程度上控制哪些 API 和功能可用。Chrome 通过 命令行开关来实现这一点,Firefox 也是如此,并且在测试工具设置中访问这些工具将允许您在 API 关闭或打开的情况下运行某些测试。(例如,请参阅 Cypress 的 browser-launch 插件 和 puppeteer 的 launch.args 参数,了解在启动时添加浏览器标志的方法。)
仅依赖用户代理字符串获取粗略信息
以另一个示例为例,自 Web 诞生之初,浏览器就在每次请求中发送对其自身的描述,即 HTTP User-Agent 标头。几乎在同样长的时间里,人们一直在告诫 Web 开发者不要使用用户代理标头的内容来为不同的浏览器提供不同的内容,而在所有这些时间里,Web 开发者仍然这样做了,在某些(但并非所有)情况下具有一定的理由。由于浏览器不希望因网站提供次优体验而被挑出来,因此这导致每个浏览器都假装是其他浏览器,而用户代理字符串看起来像这样
Mozilla/5.0 (Linux; Android 6.0.1; SGP771 Build/32.2.A.0.253; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/52.0.2743.98 Safari/537.36
.
除其他事项外,这声称是 Mozilla/5.0,这是一款浏览器,它与第一批宇航员在 20 多年前登上国际空间站的时间相同。用户代理字符串当然是指纹识别的丰富熵来源,为了减轻该指纹识别性,浏览器制造商要么已经冻结了用户代理标头,要么正在努力这样做。这是更改 API 提供的数据而不必完全删除 API 的另一个示例。发送空白用户代理标头会破坏无数假定它存在的网站。因此,总的来说,浏览器正在做的是删除其中的一些细节,然后从那时起将其大部分保持不变。(您可以在 Safari、Chrome 和 Firefox 中看到这种情况。)这种防止详细指纹识别的保护基本上意味着您不能再依赖用户代理标头是准确的,如果您这样做,那么找到替代数据来源非常重要。
需要明确的是,用户代理中的数据不会完全消失,但它的粒度较低,或者有时不准确,因为可能会报告较旧但不变的数字。例如,Firefox、Safari 和 Chrome 都将报告的 macOS 版本号上限为 10(有关更多讨论,请参阅 用户代理字符串缩减更新)。有关 Chrome 计划如何减少用户代理字符串中数据的确切细节,请访问 用户代理缩减,但简而言之,您可以预期报告的浏览器版本号将仅包含一个主要版本(因此版本号将类似于 123.0.0.0,即使浏览器版本为 123.10.45.108),并且操作系统版本将没有详细信息,并且将冻结为少量不变的选择之一。因此,一个假想的 Chrome 版本 123.45.67.89 在一个假想的“Windows 20”上运行,将报告其版本号为
Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36
您需要的核心信息(浏览器版本)仍然可用:它是 Windows 上的 Chrome 123。但是,辅助信息(芯片架构、Windows 版本、它模仿的 Safari 版本、浏览器次要版本)在冻结后将不再可用。
将此与不同平台上的“当前”Chrome 用户代理进行比较
Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36
,
可以看出,唯一不同的是 Chrome 版本号 (104) 和平台标识符。
同样,Safari 的用户代理字符串确实显示了平台和 Safari 版本号,并且还在 iOS 上给出了操作系统版本,但其他所有内容都被冻结了。因此,一个假想的 Safari 版本 1234.5.67 在一个假想的 macOS 20 上运行,可能会将其用户代理给出为
Mozilla/5.0 (Macintosh; **Intel Mac OS X 10_20_0**) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.1 Safari/605.1.15
,
而在一个假想的 iOS 20 上,它可能是
Mozilla/5.0 (iPhone; CPU **iPhone OS 20_0** like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/**20.0 Mobile/15E148 Safari/605.1.15**
.
同样,核心信息(这是 Safari,它在 iOS 或 macOS 上)可用,iOS Safari 仍然提供 iOS 版本号;但过去可用的许多辅助信息此后已被冻结。重要的是,这包括 Safari 版本号,该版本号不一定可用。
报告的用户代理的更改引起了激烈的争论。https://github.com/WICG/ua-client-hints#use-cases summarises 总结了更改的一些论点和原因,Rowan Merewood 有一个 幻灯片,其中包含一些策略,用于在 UA 客户端提示提案的上下文中,迁移远离使用用户代理进行区分,这将在后面进一步解释。
模糊化
模糊化是安全实践中的一个术语,其中使用意外值调用 API,希望它们错误地处理这些意外值并暴露安全问题。Web 开发者应该熟悉 跨站脚本 (XSS),它涉及向页面添加恶意脚本,通常是因为页面没有正确转义注入的 HTML(因此您使用文本 <script>
在其中进行搜索查询)。后端开发者将意识到 SQL 注入,其中未正确验证用户输入的数据库查询会暴露安全问题(正如 xkcd 使用 Little Bobby Tables 显着说明的那样)。模糊化或 模糊测试 更恰当地用于自动尝试向 API 提供许多不同的无效或意外输入,并检查结果是否存在安全漏洞、崩溃或其他不良处理。这些都是故意提供不准确信息的示例。但是,在这里,浏览器正在抢先这样做(通过使用户代理故意不正确),以鼓励开发者停止依赖该数据。
执行
- 检查您的代码库中是否对用户代理字符串有任何依赖性(搜索
navigator.userAgent
可能会找到客户端代码中的大多数出现,并且您的后端代码可能会查找User-Agent
作为标头),包括您的依赖项。 - 如果您在自己的代码中发现使用,请找出代码正在检查的内容,并找到另一种进行区分的方法(或找到替代依赖项,或通过提交问题或与他们联系以获取更新来与上游依赖项合作)。有时,浏览器区分是必要的,以解决错误,但一旦用户代理被冻结,它将越来越不成为执行此操作的方式。
- 您可能是安全的。如果您仅使用品牌、主要版本和平台的核心值,那么这些值几乎肯定仍然可用,并且在用户代理字符串中是正确的。
- MDN 描述了避免依赖用户代理字符串 (“浏览器嗅探”) 的好方法,其中主要方法是功能检测。
- 如果您在某种程度上依赖用户代理字符串(即使在使用仍然有用的少数核心值时),最好使用即将推出的新浏览器版本中的用户代理进行测试。可以使用那些即将推出的浏览器版本本身通过 beta 版或技术预览版进行测试,但也可以设置自定义用户代理字符串进行测试。您可以在 Chrome、Edge、Firefox 和 Safari 中覆盖用户代理字符串,在进行本地开发时,为了检查您的代码如何处理您可能从用户那里收到的不同用户代理值。
客户端提示
提供此信息的一个主要提案是 用户代理客户端提示,尽管并非所有浏览器都支持此提案。支持的浏览器将传递三个标头:Sec-CH-UA
,它提供浏览器品牌和版本号;Sec-CH-UA-Mobile
,它指示请求是否来自移动设备;以及 Sec-CH-UA-Platform
,它命名操作系统。(解析这些标头并不像看起来那么容易,因为它们是 结构化标头,而不是简单的字符串,这是由浏览器发送“棘手”值来强制执行的,如果不正确解析,这些值将被错误处理。正如之前所述,这是浏览器抢先进行“模糊测试”的一个示例。使用此数据的开发者需要正确处理它,因为数据是这样设计的,即不正确或草率的解析可能会给出错误的结果,例如显示不存在的品牌,或未正确关闭的字符串。)幸运的是,浏览器也通过 JavaScript 直接将此数据提供为 navigator.userAgentData
,在支持的浏览器中,它可能看起来像这样的对象
{
"brands": [
{
"brand": " Not A;Brand",
"version": "99"
},
{
"brand": "Chromium",
"version": "96"
},
{
"brand": "Google Chrome",
"version": "96"
}
],
"mobile": false
}