浏览器指纹可以通过浏览器对网站可见的配置、设置信息,来跟踪Web浏览器,它就像我们人手上的指纹一样,具有个体辨识度,只不过现阶段浏览器指纹辨别的是浏览器。
浏览器指纹辨识的信息可以是UA、时区、地理位置或者是使用的语言等等,浏览器所开发的信息决定了浏览器指纹的准确性。
对于网站而言,拿到浏览器指纹并没有实际价值,真正有价值的是浏览器指纹对应的用户信息。作为网站站长,收集用户浏览器指纹并记录用户的操作,是一个有价值的行为,特别是针对没有用户身份的场景。
例如一个视频网站,未注册该网站的用户A喜欢浏览二次元的视频,通过浏览器指纹记录这个,那么下次可以直接向该浏览器推送二次元的视频。因为现在的上网设备大都是私人的,这样的推送方式很容易获得大部分用户的好感,从而使之成为网站的用户。
浏览器指纹的发展
浏览器指纹技术的发展跟大多数技术一样,并非一蹴而就的,现有的几代浏览器指纹技术是这样的:
目前浏览器指纹的追踪技术可以算是进入2.5代,这么说是因为跨浏览器识别指纹的问题仍没有解决。
信息熵(entropy)是接收的每条消息中包含的信息的平均量,信息熵越高,则能传输越多的信息,信息熵越低,则意味着传输的信息越少。
浏览器指纹是由许多浏览器的特征信息综合起来的,其中特征值的信息熵也不尽相同。因此,指纹也分为基本指纹和高级指纹。
基本指纹
拿到这些值后可以进行一些运算,得到浏览器指纹具体的信息熵以及浏览器的uuid。
这些信息就类似人类的体重、身高、肤色一样,有很大的重复概率,只能作为辅助识别,所以我们需要更精确的指纹来判断唯一性。
高级指纹
普通指纹是不够区分独特的个人,这时就需要高级指纹,将范围进一步缩小,甚至生成一个独一无二的跨浏览器身份。
用于生产指纹的各个信息,有权重大小之分,信息熵大的将拥有较大的权重。
从该论文中可以看出,时区、屏幕分辨率和色深、Canvas、webGL的信息熵在跨浏览器指纹上的权重是比较大的。下面我们就来看看这些高级指纹都包含了些什么信息。
Canvas指纹
Canvas是HTML5中的动态绘图标签,也可以用它生成图片或者处理图片。即便使用Canvas绘制相同的元素,但是由于系统的差别,字体渲染引擎不同,对抗锯齿、次像素渲染等算法也不同,Canvas将同样的文字转成图片,得到的结果也是不同的。
实现代码大致为:在画布上渲染一些文字,再用toDataURL转换出来,即便开启了隐私模式一样可以拿到相同的值。
WebGL指纹
WebGL(Web图形库)是一个JavaScriptAPI,可在任何兼容的Web浏览器中渲染高性能的交互式3D和2D图形,而无需使用插件。WebGL通过引入一个与OpenGLES2.0非常一致的API来做到这一点,该API可以在HTML5元素中使用。这种一致性使API可以利用用户设备提供的硬件图形加速。网站可以利用WebGL来识别设备指纹,一般可以用两种方式来做到指纹生产:
WebGL报告——完整的WebGL浏览器报告表是可获取、可被检测的。在一些情况下,它会被转换成为哈希值以便更快地进行分析。
WebGL图像——渲染和转换为哈希值的隐藏3D图像。由于最终结果取决于进行计算的硬件设备,因此此方法会为设备及其驱动程序的不同组合生成唯一值。这种方式为不同的设备组合和驱动程序生成了唯一值。
可以通过Browserleakstest检测网站来查看网站可以通过该API获取哪些信息。
产生WebGL指纹原理是首先需要用着色器(shaders)绘制一个梯度对象,并将这个图片转换为Base64字符串。然后枚举WebGL所有的拓展和功能,并将他们添加到Base64字符串上,从而产生一个巨大的字符串,这个字符串在每台设备上可能是非常独特的。
例如fingerprint2js库的WebGL指纹生产方式:
混淆Canvas指纹
我们已经了解了是如何获取canvas指纹的,那么应该如何防范被恶意获取呢?想混淆Canvas指纹,只需要在toDataURL得到的结果上做手脚就可以。
toDataURL()将整个canvas的内容导出,我们需要将Canvas中的部分内容修改,这个时候可以通过getImageData()复制画布上指定矩形的像素数据,然后通过**putImageData()**将图像数据放回,然后再使用toDataURL()导出的图片就有了差异。
CanvasRenderingContext2D.getImageData()返回一个ImageData对象,用来描述Canvas区域隐含的像素数据。这个区域通过矩形表示,起始点为(sx,sy)、宽为sw、高为sh。
ImageData接口描述了元素的一个隐含像素数据的区域,可以由ImageData()方法构造,或者由canvas在一起的CanvasRenderingContext2D对象的创建方法:createImageData()和getImageData()。
ImageData对象存储着canvas对象真实的像素数据,它包含几个只读属性:
Uint8ClampedArray类型的一位数组,包含着RGBA的整型数据,范围在0255。它可以视作初始像素数据,每个像素用4个1bytes值(按照red、green、blue、alpha的顺序),每个颜色值用0255中的数字代表。每个部分被分配到一个数组内的连续索引,左上角第一个像素的红色部分,位于数组索引的第0位。像素从左到右从上到下被处理,遍历整个数组。
Unit8ClampedArray包含高度宽度4bytes数据,索引值从0~(wh4)-1。
例如,读取图片中位于第50行,200列的像素的蓝色部分,则:
1constblueComponent=imageData[50*(imageData.width*4)+200*4+2]下面是实现混淆Canvas指纹的方法:
与前面混淆canvas指纹混淆的思路是一致的,都是更改被获取对象的原型的方法。
比如混淆时区,就是更改Date.prototype.getTimezoneOffset的返回值。
混淆分辨率则是更改documentElement.clientHeightdocumentElement.clientWidth
混淆WebGL则要更改WebGLbufferDatagetParameter方法等等。
当然,我们也有一些简单的方法来防止被生成用户指纹。例如我们可以通过浏览器的扩展插件(CanvasBlocker、WebGLFingerprintDefender、FingerprintSpoofing等),在网页加载前执行一段JS代码,更改、重写JS的各个函数来阻止网站获取各种信息,或返回一个假的数据,以此来保护我们的隐私信息。