简介
人的指纹千变万化,具有唯一性,可以作为人的身份标识。同时人的姓名、身份证号、相貌特征也可以作为唯一的身份标识。
设备指纹或机器指纹是为了识别而收集的有关远程计算设备的软件和硬件的信息。比如设备的硬件ID,像手机在生产过程中都会被赋予一个唯一的IMEI(International Mobile Equipment Identity)编号,用于唯一标识该台设备。像电脑的网卡,在生产过程中会被赋予唯一的MAC地址。
通常使用指纹算法将信息同化为一个简短的标识符。
而浏览器指纹就是指通过与设备的web浏览器交互而专门收集的信息。
浏览器厂商对于指纹的收集行为是非常抵触的,认为侵犯了用户的隐私。部分浏览器,例如chorme等会有一些策略进行限制。
作用
理论上,无登录的状态下,并且浏览器中无法读取或存储持久cookie,无法读取客户端的IP,但是仍然可以通过浏览器指纹完全或者部分识别单个设备。
- 使服务提供商能够检测并防止身份盗窃和一些欺诈行为
- 组成个人浏览历史的长期记录(并提供有针对性的广告甚至是对目标发起相对应的攻击行为)
获取
社区成熟的解决方案,fingerprintjs.分为免费版和商用版。免费版识别率为40-60%,商用版本为99.5%。
demo地址: fingerprintjs.github.io/fingerprint…
// Initialize the agent at application startup.
// If you're using an ad blocker or Brave/Firefox, this import will not work.
// Please use the NPM package instead: https://t.ly/ORyXk
const fpPromise = import('https://openfpcdn.io/fingerprintjs/v4')
.then(FingerprintJS => FingerprintJS.load())
// Get the visitor identifier when you need it.
fpPromise
.then(fp => fp.get())
.then(result => {
// This is the visitor identifier:
const visitorId = result.visitorId
console.log(visitorId)
})
原理
指纹是多个维度的特征,按照一定的算法聚合后的结果。通常分为以下两种指纹
通过上面的思维导图,可以看出浏览器指纹的一个大概获取思路。就是聚合用户大量的信息,对用户进行标识。之所以指纹被区分为基本指纹和特征指纹的原因是在于,基本指纹的重复率是相当高的。如果基于基本指纹信息去标识用户很明显是没有太大意义的。
打个比方就是:我的谷歌电脑的UA,时区、电脑系统版本等可能和用户A,用户B是一摸一样的。所以通过基本指纹只能圈定出一批人,但是不能精准的定位一个人。
对于特征指纹来说,它的生成结果是由多种不同的因素导致的。以canvas指纹举例:它的复杂性在于它可以通过绘制操作反映出底层系统的许多特征,包括但不限于 GPU 渲染特征、系统字体、浏览器的细微差异等。
这也就是说:虽然用户A和用户B即使基本指纹信息一致,但是生成的canvas指纹也是不一致的。因为两个用户之间的驱动版本,字体,硬件等等撞车的概率是比较低的。
虽然canvas指纹撞车的概率很低,但是当用户量足够大了后。相同的情况还是有可能出现的,毕竟林子大了什么鸟都有。为了最大的可能规避掉这种问题,又引入了一种综合指纹的概念。
世界上没有两片相同的树叶。综合指纹,简单的来说就是将用户多个指纹的维度再次进行聚合。如果我们的基本指纹一致,那么我们再聚合一个canvas指纹,如果还是一致的,那么我们再聚合一个audio指纹直到将我们区分出来。
接下来简单介绍一下常见的指纹获取方式:
基本信息指纹
基本信息指纹的获取主要就是获取浏览器window上的一些对象的属性值。比如navigator、screen等。通过这些对象可以获取到例如:用户UA,浏览器语言,屏幕设备信息,时区信息等等特征。
这些信息通过在浏览器中执行js脚本获取对象属性值,或者调用一定的方法即可。这些信息也是最容易被定制和篡改的。
主流网站基本都会通过埋点的方式去获取用户浏览器里面的这些对象信息,以供产品参考迭代。
canvas指纹
以某网站为例,通过打断点调试源代码可以发现是通过canvas进行指纹的收集。如下图所示,可以看到在我个人的谷歌浏览器上,对我生成的指纹值为 8835...
实际上canvas指纹的获取原理就是,离屏画一张图片,这张图片用户不可见。通过获取图片的base64等信息,按照网站自己的算法算出一个hash值。因为图片的绘制几乎涉及到了用户电脑上的所有绘图特征(gpu、绘图驱动等)。所以在不同用户的电脑上绘制一张看起来一模一样的图片,实际上获取的图片信息也是有所不同的。通过这种手段标识一个用户是非常有效的。
通过查看网页源代码可以发现,其绘制的图片一张如下所示的图片。然后通过这张图片的信息算出了我的指纹是 8835...
以下是一些可能的手段伪造canvas信息:
使用统一的硬件和软件环境: 确保所有设备使用相同的操作系统版本、浏览器版本、字体集和驱动程序。
WebGL层面的干预: 如果 Canvas 指纹使用了 WebGL API,那么可能需要覆盖相关的 WebGL 函数以返回预先设定的数据。
系统字体的统一: 由于系统字体是 Canvas 指纹的一个组成部分,因此需要确保所有设备具有相同的字体库。
使用专门工具: 使用专门的隐私保护工具或服务,这些工具可能会提供更深层次的伪造功能,包括修改或阻止对硬件加速的调用。
操作系统级别的虚拟化: 使用虚拟机或容器化技术来运行统一配置的操作系统和浏览器,确保所有虚拟环境对外显示相同的特征。
无论如何,伪造 Canvas 指纹仍然是一个挑战,因为要完全模仿另一台设备的所有绘图特性,几乎需要在所有可能影响指纹的层面上实现一致性。
audio指纹
以某视频网站为例,通过调试源代码可以获取。它是通过生成音频方式获取audio指纹。如下所示,在我个人的谷歌浏览器上,某网站对我生成的指纹值为 124.043...
如下所示:即使我切换了无痕模式,重新获取依然是这个值。
与上面的网站不同,这个网站获取的音频指纹。音频指纹的获取原理是,生成一段音频,对这段音频进行解码。在这段音频还没有被播放出来的时候就断开音频的链接。这样用户是不会听到这段声音的。通过对声音的解码可以获取到音频的信息。再基于网站本身的算法逻辑就可以算出指纹。和canvas指纹类似,音频指纹的生成也是基于了用户硬件和驱动程序等等。所以音频指纹相同的概率也是非常低的。
webGL指纹
原理同canvas指纹类似,在此不再赘述。
webRTC指纹
WebRTC(Web Real-Time Communication)是一种使得网页能够进行实时通信(如视频和音频聊天)的技术。WebRTC 可以泄露用户的真实 IP 地址,即使用户正在使用 VPN。这可以成为一种用来追踪用户的指纹技术。
在谷歌浏览器中,有相应的配置项目可以关闭这个选项。如果关闭可能导致某些视频网站等无法使用。
混淆指纹
想要定制一个指纹是困难的事情,因为用户的使用环境千差地别,很多硬件等因素很难统一。但是想要混淆一个指纹是可行的。比如我不想网站收集到我真实的指纹,就可以基于上面了解到的原理进行定制化的处理。以下列举一些可行的方式,能够混淆指纹:
对于基本的指纹信息,例如浏览器语言,用户UA,时区等等。可以通过js proxy代理技术或者篡改js原生方法来实现。
完整的源代码在此 ,如有需要可以交流研究。
以下是部分实现逻辑:
一个代理navigator的实现:
一个时区修改的实现:
最后需要提醒的是,任何试图修改或伪装浏览器指纹的行为都应该在遵守当地法律和网站政策的前提下进行。