FingerprintJS分析

申屠健
2023-12-01

分析变量

webAudio(getAudioFingerprint)

指纹随 浏览器内核 变化。

博客文章

官方解释

cookiesEnabled——是否启用了cookie

指纹随 浏览器设置 变化。

实则是设置cookie后尝试取得刚刚设置的cookie,如果能取到说明cookie功能被启用了。可以作为保守策略使用,因为它不会常常变动并且只有少数用户会改动它。也正因此,它的熵值并不高。

colorGamut——检测显示设备支持的色域

指纹随 显示设备的硬件参数 变化。

利用媒体查询中的color-gamut来判断色域支持情况。脚本会依次利用’rec2020’, ‘p3’, 'srgb’作为参数创建matchMedia实例,并使用实例方法matches来判断显示设备是否支持这个色域。显示器支持的最高色域会作为色域指纹。

对于只有一台显示设备的用户来说,这个值不太会发生变化,但是对于多个显示器的用户来说,当页面处于两个显示器上时,色域指纹可能是不同的。

invertedColors——检测设备是否开启了颜色反相

指纹随 操作系统设置 变化。

这是通过媒体查询invertedColors检测的,然而目前只有Safari一家支持这个媒体查询,也就是说其他浏览器的颜色反相指纹都是undefined。并且颜色反相主要是为了方便残疾人的,大多数用户不会开它。所以这一项也是 十分保守 的。

forcedColors——检测设备是否开启了强制颜色

排除掉这个指纹,因为不清楚这个设置的含义。详情参见

monochrome——像素位数

指纹随 显示设备的硬件参数 变化。

但是如果显示设备不是单色设备的话,这个指纹的值是0。它和色彩滤镜是不同的,它查询的是设备属性,也就是说即使我把mac设置成单色显示,这个值仍然是0。可能如果我找个黑白设备来做实验的话它的值会是1。

contrast——对比度

指纹随 操作系统设置 变化。

这项设置只在safari 14.1及以后的版本支持。在偏好设置中找到辅助功能=》显示=》提高对比度,会让值发生变化。

reducedMotion——动画减弱功能

指纹随 操作系统设置 变化。

用户偏好设置如何影响此指纹,可以参考这篇文档

hdr——支持HDR

指纹随 显示设备的硬件参数 变化。

这项本质上是通过媒体查询判断dynamic-range,因此指纹是随着显示器变化而变化的。在支持高对比度+24位以上色深+高峰值亮度上dynamic-range的值是true,反之false。

math——计算表现

指纹随 操作系统版本 变化

Math这个库在不同的操作系统上对特定值会有不同的计算结果。例如在32位的Linux系统上Math.exp(10)的计算结果是22026.465794806725,而在64位Linux系统上计算结果是22026.465794806718。具体参考这里这里

vendorFlavors——全局变量

指纹随 浏览器引擎 变化。

浏览器引擎往往会在window上面挂载特定的全局变量,例如IOS系统上运行的Chrome浏览器会挂载名为__crWeb的变量,安卓上运行的UC浏览器会挂载名为ucweb的变量。通过这种差异,可以将不同浏览器引擎之间做出一些区分。

vendor——浏览器供应商名称

指纹随 浏览器供应商 变化。

使用navigator.vendor来获取浏览器供应商名称。

touchSupport——触摸设备检测

硬件设备热插拔 变化。

依据这篇文章的描述,其实并没有一个很靠谱的方法来检测触摸屏。而且,即使有这种方法,那对于桌面设备来说,用户插拔触摸设备会让指纹立刻发生变化,这会影响用户体验。

canvas——绘制图形

在画布上画文字和几何图形,之后将图形导出成base64。base64记录的是每个像素的信息。不同的操作系统、显卡驱动等都会影响这个指纹。详细请参考这篇文档

plugins——检查插件信息

这项使用到了navigator.plugins这个api,然而此api已经被弃用了。因此这个指纹应该被剔除。可是考虑到这一项指纹确实可以比较好的反映出用户的个性化配置。最终决定保留。

此项计算的参数包括插件的description、name、type、suffixes四项信息。

platform——平台

用来区分平台。然而其主要api(navigator.platform)已经被弃用了,不一定什么时候就不能用了。也许可以使用 bowser 这个包来做个补充,但不能替代,因为bowser这个包是通过ua来分析平台,而navigator.platform是浏览器自己的api。

cpuClass——cpu等级

不知道这究竟是个什么东西。mdn里面也没有相关信息。

openDatabase

指纹随 浏览器 变化。

检测是不是有window.openDatabase这个api。

indexedDB

指纹随 浏览器 变化。

检测是不是有window.indexedDB这个api。

localStorage

指纹随 浏览器 变化。

检测是不是有window.localStorage这个api。

sessionStorage

指纹随 浏览器 变化。

检测是不是有getSessionStorage这个api。

timezone——当前时区或当前时间与UTC的偏移量

指纹随 时区 变化

如果浏览器支持Intl命名空间的话,就使用此命名空间内的api来计算当前时区,比如’Asia/Shanghai’,并以此为指纹。如果没有此命名空间则会计算当前计算机时间与UTC时间之间的偏移量,并以此为指纹。

hardwareConcurrency——硬件并发数

指纹随 硬件设备 变化

通过navigator.hardwareConcurrency获取当前设备的并发数,比如我这台mac的cpu是i3,所以并发数是4。

screenResolution——屏幕的分辨率

指纹随 显示器 变化

通过screen.width和screen.height获取屏幕宽高,并以此作为指纹。

deviceMemory——内存大小

指纹随 硬件设备 变化

通过navigator.deviceMemory获取内存大小,并以此作为指纹。

colorDepth——色彩深度

指纹随 显示器 变化

通过window.screen.colorDepth获取显示设备的色彩深度,并以此作为指纹。

languages——语言

指纹随 多种设置 变化

这一项除了使用navigator.languages取值外,还会依次从navigator.languages、navigator.userLanguage、navigator.browserLanguage、navigator.systemLanguage中按顺序取出第一项存在的设置。这就意味着影响此项指纹的变量是比较多的。

osCpu——cpu信息

此项使用到的api已经不在受支持,详情参见。因此决定剔除掉这一项。

screenFrame——可用的屏幕分辨率

指纹随 显示器 变化。如果用户在pc浏览器上开启移动设备模拟器的话,这个值也会变化。

不同显示器的屏幕分辨率是不同的,对于多显示器用户而言,将浏览器窗口在两台显示器之间拖拽会引起指纹变化。

除此之外,当浏览器被设置为全屏之后,由于顶部工具条占用的空间被分给了浏览器,可用分辨率就变化了,于是指纹也会变化。

但是浏览器最大化和最小化是不影响这个值的。

上面几条只是从源码中得到的启示,具体表现需要测试才能知道。因为我在macos的chrome上做测试时候发现一个关键api——document.fullscreenElement根本没生效,即使我已经开启了全屏模式。

这个地方牵扯到包作者自己写的任务管理模块,这个模块的逻辑没有摸透,它会对指纹有影响。

domBlockers——检测dom节点是否会被屏蔽

脚本会创建一系列可能会被屏蔽的dom节点。然后检测这些节点有没有被屏蔽掉。这些节点的class、id或是跳转链接等属性可能包含赌博与广告相关的关键字。这有可能被浏览器或浏览器插件屏蔽。

我们可以自定义需要被生成的dom节点。

fonts——字体

指纹随着 可用字体 变化。同一台设备且是同一个浏览器的情况下,更改系统默认字体、__安装新字体__都 可能 会引起指纹变化。

影响字体是否可用的因素我是不清楚的。但能肯定的是,这绝不是单一因素可以决定的。甚至前端在引入与使用字体时设置的font-style不一致也会导致指定字体在 某些版本的系统 上无法显示。虽然没办法分析影响字体可用的因素,但有一种方法可以检测到客户机上究竟有没有使用我们指定的字体。

讲原理前要知道浏览器使用字体的规则。我们在写全局默认字体的时候通常会给font-family设置一长串的字体名称,这是因为浏览器会从左到右依次尝试使用这些指定字体,直到遇到一个可以使用的为止。除此之外我们还会选择’monospace’, ‘sans-serif’, 'serif’的其中一项作为最后一个值,这三个值不是任何一种特定的字体名称,而是分别对应了等宽字体、非衬线字体、衬线字体三种字体家族。这三种字体家族具体在系统中对应了哪个字体是由用户设置的(或系统默认设置)。也就是说,只要我们写了三者其一,那浏览器就一定会从这个字体家族里面取出一个默认字体来,从而保证了文本一定可以被显示在页面上。

了解上述这些之后再谈检测方法就轻松了。首先,分别计算使用’monospace’, ‘sans-serif’, 'serif’三种默认字体显示测试文本时的DOM宽高。然后我们分别将上述三种默认字体与指定字体组合,并计算三种组合下显示测试文本时DOM的宽高,这么做的理由在于,如果指定字体没被使用,那么宽高一定和默认字体是一致的,反之则不一致。因此最后只需要对比两个宽高就可以知道指定字体有没有被使用。依次对每个指定字体做上述操作,就可以得到一个可用字体的列表。最终会以此为字体列表作为指纹。

对计算变量的补充

目前包里面的指纹对相同设备的区分能力并不强,因为同样一台设备买回来,很多用户是不会去做个性化设置的,尤其是刻意卖号的人。他们为了同一个token能多设备用,那必然不会做任何个性化设置,这样就能保证指纹相同。为了尽可能的避免这种情况,我希望尝试加入几个指纹。

为了区分移动设备:在本地存个随机字符串

一直以来都有个问题,那就是如果是同样型号的手机在同一个浏览器内(例如微信浏览器)去浏览我们的项目的话,指纹大概率是一模一样的。 因为我们的设备、屏幕、cpu等等一系列检测统统起不到区分设备的效果,插件什么的一般都是没有的。

这时候我可以手动给每个浏览器加个特殊的属性。比如我在localStorage里面放个数据,这个数据就是用时间戳和随机数生成的一串id,我每次计算指纹的时候都带上这个id,这个id就是用来增加熵值用的。

这个指纹一旦存在在local里面,就不再重复设置,如果不存在就生成它。

参考

MDN的@media文档

 类似资料: