我们常说的指纹,都是指人们手指上的指纹,因具有唯一性,所以可以被用来标识一个人的唯一身份。而浏览器指纹是指仅通过浏览器的各种信息,如 CPU 核心数、显卡信息、系统字体、屏幕分辨率、浏览器插件等组合成的一个字符串,就能近乎绝对定位一个用户,就算使用浏览器的隐私窗口模式,也无法避免。
这是一个被动的识别方式。也就是说,理论上你访问了某一个网站,那么这个网站就能识别到你,虽然不知道你是谁,但你有一个唯一的指纹,将来无论是广告投放、精准推送、安全防范,还是其他一些关于隐私的事情,都非常方便。
浏览器基本指纹是任何浏览器都具有的特征标识,比如屏幕分辨率、硬件类型、操作系统、用户代理(User agent)、系统字体、语言、浏览器插件 、浏览器扩展、浏览器设置 、时区差等众多信息,这些指纹信息“类似”人类的身高、年龄等,有很大的冲突概率,只能作为辅助识别。可以在该网址进行查看本地浏览器的基本特征,https://www.whatismybrowser.com/
浏览器高级指纹与基本指纹的区别是,基本指纹就像是人的外貌特征,外貌可以用男女、身高、体重区分,然而这些特征不能对某个人进行唯一性标识,仅使用基本指纹也无法对客户端进行唯一性判定,基于 HTML5 的诸多高级功能就能生成高级指纹了。
Canvas 指纹
说到高级指纹,不得不提 Canvas 指纹,Canvas(画布)是 HTML5 中一种动态绘图的标签,可以使用其生成甚至处理高级图片。
Canvas 指纹的原理大致如下:
相同的 HTMLCanvasElement 元素绘制操作,在不同操作系统、不同浏览器上,产生的图片内容不完全相同。在图片格式上,不同浏览器使用了不同的图形处理引擎、不同的图片导出选项、不同的默认压缩级别等。在像素级别来看,操作系统各自使用了不同的设置和算法来进行抗锯齿和子像素渲染操作。即使相同的绘图操作,产生的图片数据的 CRC 检验也不相同。Canvas 几乎已被所有主流浏览器支持,可以通过大部分的 PC、平板、智能手机访问。
在线测试地址:https://www.browserleaks.com/canvas, 可查看浏览器的 Canvas 唯一性字符串。
WebGL 指纹
通过 HTMLCanvasElement 元素可以获取到 Webgl 对象(canvas.getContext(“webgl”)),通过此对象可以获取到用户的硬件信息,比如显卡名称、显卡型号、显卡制造商等,比如:ANGLE (NVIDIA GeForce GTX 1050 Ti Direct3D11 vs50 ps50),Google Inc.。
由于硬件一般是不会随意更换的,有些是电脑买来到电脑报废就没更换过硬件,电脑硬件种类也比较多,虽然非常大的碰撞率,但是依然可以被用来当做用户指纹的一部分,收集用户的信息也多,就越能代表用户的唯一指纹,这点不可忽视。
AudioContext 指纹
HTML5 提供给 JavaScript 编程用的 Audio API 则让开发者有能力在代码中直接操作原始的音频流数据,对其进行任意生成、加工、再造,诸如提高音色,改变音调,音频分割等多种操作,甚至可称为网页版的 Adobe Audition。
AudioContext 指纹原理大致如下:
方法一:生成音频信息流(三角波),对其进行 FFT 变换,计算 SHA 值作为指纹。
方法二:生成音频信息流(正弦波),进行动态压缩处理,计算 MD5 值。
两种方法都是在音频输出到音频设备之前进行清除,用户根本就毫无察觉就被获取了指纹。
AudioContext 指纹基本原理:
主机或浏览器硬件或软件的细微差别,导致音频信号的处理上的差异,相同器上的同款浏览器产生相同的音频输出,不同机器或不同浏览器产生的音频输出会存在差异。
从上可以看出 AudioContext 和 Canvas 指纹原理很类似,都是利用硬件或软件的差异,前者生成音频,后者生成图片,然后计算得到不同哈希值来作为标识。音频指纹测试地址:https://audiofingerprint.openwpm.com
WebRTC 指纹
WebRTC(网页实时通信,Web Real Time Communication),是可以让浏览器有音视频实时通信的能力,它提供了三个主要的 API 来让 JS 可以实时获取和交换音视频数据,MediaStream、RTCPeerConnection 和 RTCDataChannel。当然如果要使用 WebRTC 获得通信能力,用户的真实 ip 就得暴露出来(NAT 穿透),所以 RTCPeerConnection 就提供了这样的 API,直接使用 JS 就可以拿到用户的 IP 地址。用户的内网 IP 地址也是大多数情况下不会改变,所以也是可以用来当做用户指纹的其中一个因子。
上面几点都说了浏览器指纹大致有哪些,还没有完全说完,只是一部分,但是零散的指纹信息并不能真正的定位到唯一用户,并不能用来代表一个用户的唯一身份(用户指纹)。
综合指纹是指将所有的用户浏览器信息组合起来,就可以近乎 99%以上的准确率定位标识用户,综合指纹大致有如下:
将以上几点组合起来就可以生成综合指纹(用户指纹),就可以达到前面说的 99%以上可以定位唯一用户。
前面我们说了一大堆网站如何使用各种技术来“生成”用户指纹,来标识唯一用户,那么下面我们来说说,如何避免被网站“生成”唯一用户指纹。
常用的手段是,通过浏览器的扩展插件,阻止网站获取各种信息,或者返回个假的数据,这种方式是在网页加载前就执行一段 JS 代码,更改、重写、HOOK 了 JS 的各个函数来实现的,因为 JS 的灵活性给这种方式提供的可能。但是这种方式始终是表层的,使用 JS 修改是能防止大部分网站的生成唯一指纹,但是是有手段可以检测出来是否“作弊”的。
更好的手段是从浏览器底层做处理,从浏览器底层修改 API 使得这些在 js 层获取的信息并不唯一,不管如何组合都不能生成一个唯一的代表用户的指纹。比如:猫头鹰浏览器
猫头鹰浏览器是基于 chromium 代码修改编译的浏览器,从底层对各种 API 做了修改,可以交给用户自定义返回各种数据,比如 Canvas、Webgl、AudioContext、WebRTC、字体、UserAgent、屏幕分辨率、CPU 核心数、内存大小、插件信息、语言等信息,这样就可以完全避免被“生成”唯一用户指纹了。
由于不同的系统显卡绘制 canvas
时渲染参数、抗锯齿等算法不同,因此绘制成图片数据的 CRC
校验也不一样。
function getCanvasFp () {
const canvas = document.getElementById('canvas')
const ctx = canvas.getContext('2d')
ctx.font = '14px Arial'
ctx.fillStyle = '#ccc'
ctx.fillText('hello, LBJ辉', 2, 2)
return canvas.toDataURL('image/jpeg')
}
因此根据 canvas
可以获取浏览器指纹信息。
canvas
,获取 base64
的 dataurlmd5
摘要计算,得到指纹信息但是对于常见的需求就有成熟的解决方案,若在生产环境使用,可以使用以下库 fingerprintjs2
它依据以下信息,获取到浏览器指纹信息,「而这些信息,则成为 component
」
canvas
webgl
UserAgent
AudioContext
requestIdleCallback(function () {
Fingerprint2.get((components) => {
const values = components.map((component) => component.value)
const fp = Fingerprint2.x64hash128(values.join(''), 31)
})
})
在 fingerprintjs2
中,对于 component
也有分类
component
同一设备跨浏览器也可以得到相同的值,有些独立浏览器,得到不同的值component
刷新后值就会发生变化,称为不稳定组件在实际业务中,可根据业务选择合适的组件
const options = {
excludes: {userAgent: true, language: true}
}