我们今天就来深入了解下浏览器指纹。

java 浏览器指纹实现 浏览器指纹技术_信息熵

浏览器指纹是什么?

浏览器指纹可以是UA,失去,地理位置或者使用的语言等,网站可以通过浏览器指纹获取到对应的使用者用户信息,能识别用户和记录用户的操作,进行个性化推荐。

现有的浏览器指纹技术,由于目前跨浏览器识别指纹的问题尚未解决,可认为发展到 处于2.5 代:

  • 第一代是状态化的,主要集中在用户的 cookie 和 evercookie 上,需要用户登录才可以得到有效的信息。
  • 第二代才有了浏览器指纹的概念,通过不断增加浏览器的特征值从而让用户更具有区分度,例如 UA、浏览器插件信息等
  • 第三代是已经将目光放在人身上了,通过收集用户的行为、习惯来为用户建立特征值甚至模型,可以实现真正的追踪技术。但是目前实现比较复杂,依然在探索中。

采集与分析指纹

浏览器指纹也分为基本指纹和高级指纹,由许多浏览器的特征信息综合起来的,不同特征值的信息熵有异。可以通过 Browserleaks 来查看浏览器指纹的情况。FingerprintJS 是一个浏览器指纹识别库,它查询浏览器属性并从中计算哈希访问者标识符。

信息熵(entropy)是接收的每条消息中包含的信息的平均量,信息熵越高,则能传输越多的信息,信息熵越低,则意味着传输的信息越少。

基本指纹就是容易被发现和修改的部分,比如:

  • 每个浏览器的UA
  • 浏览器发送的 HTTP ACCEPT 标头
  • 浏览器中安装的浏览器扩展/插件,例如 Quicktime,Flash,Java 或 Acrobat,以及这些插件的版本
  • 计算机上安装的字体。
  • 浏览器是否执行 JavaScript 脚本
  • 浏览器是否能种下各种 cookie 和 “super cookies”
  • 是否浏览器设置为“Do Not Track”
  • 系统平台(例如 Win32、Linux x86)
  • 系统语言(例如 cn、en-US)
  • 浏览器是否支持触摸屏
  •  http 的 header
{  
  "headers": {    
    "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3",     
    "Accept-Encoding": "gzip, deflate, br",     
    "Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8",     
    "Host": "httpbin.org",     
    "Sec-Fetch-Mode": "navigate",     
    "Sec-Fetch-Site": "none",     
    "Sec-Fetch-User": "?1",     
    "Upgrade-Insecure-Requests": "1",     
    "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36"
  }}

经过运算,得到浏览器指纹具体的信息熵以及浏览器的 uuid,由于基本指纹的重复率较高,只能作为辅助识别,所以人们需要更精确的高级指纹来判断唯一性。甚至生成一个独一无二的跨浏览器身份。

参考论文《Cross-Browser Fingerprinting via OS and Hardware Level Features》,像时区、屏幕分辨率和色深、Canvas、webGL 的信息熵在跨浏览器指纹上的权重是比较大的:

 

java 浏览器指纹实现 浏览器指纹技术_WebGL_02

接下来我们来详细看这些高级指纹都包含了些什么信息。

Canvas 指纹

Canvas 是 HTML5 中的动态绘图标签,也可以用它生成图片或者处理图片。即便使用 Canvas 绘制相同的元素,但是由于系统的差别,字体渲染引擎不同,对抗锯齿、次像素渲染等算法也不同,Canvas 将同样的文字转成图片,得到的结果也是不同的。

通过在网站上执行 Canvas 渲染代码,在画布上渲染一些文字,再用 toDataURL 转换出来,如此针对不同浏览器,Canvas 结果不尽相同:

function getCanvasFingerprint () {    
    const canvas = document.createElement('canvas');    
    const context = canvas.getContext("2d");    
    context.font = "18pt Arial";    
    context.textBaseline = "top";    
    context.fillText("Hello, user.", 2, 2);    
    return canvas.toDataURL("image/jpeg");
}
getCanvasFingerprint();

WebGL 指纹

WebGL(Web图形库)是一个 JavaScript API,可在任何兼容的 Web 浏览器中渲染高性能的交互式 3D 和 2D 图形,而无需使用插件。WebGL 通过引入一个与 OpenGL ES 2.0 非常一致的 API 来做到这一点,该 API 可以在 HTML5 元素中使用。这种一致性使 API 可以利用用户设备提供的硬件图形加速。

网站可以利用 WebGL 来识别设备指纹,一般可以用两种方式来做到指纹生产:

  • WebGL 报告——完整的 WebGL 浏览器报告表是可获取、可被检测的。在一些情况下,它会被转换成为哈希值以便更快地进行分析。
  • WebGL 图像 ——渲染和转换为哈希值的隐藏 3D 图像。由于最终结果取决于进行计算的硬件设备,因此此方法会为设备及其驱动程序的不同组合生成唯一值。这种方式为不同的设备组合和驱动程序生成了唯一值。

产生WebGL指纹原理是首先需要用着色器(shaders)绘制一个梯度对象,并将这个图片转换为Base64字符串。然后枚举 WebGL 所有的拓展和功能,并将他们添加到Base64字符串上,从而产生一个巨大的字符串,这个字符串在每台设备上可能是非常独特的。比如 fingerprintjs库的 WebGL 指纹生产。

如何防止被网站采集“浏览器指纹”

浏览器指纹可能涉及到隐私泄露,如果不想被网站获取,是需要一些方法来阻止网站的。

通过浏览器的扩展插件(Canvas Blocker、WebGL Fingerprint Defender、Fingerprint Spoofing等),在网页加载前执行一段 JS 代码,更改、重写 JS 的各个函数来阻止网站获取各种信息,或返回一个假的数据,以此来保护我们的隐私信息:

  • 混淆时区,就是更改 Date.prototype.getTimezoneOffset 的返回值。
  • 混淆分辨率则是更改documentElement.clientHeight documentElement.clientWidth
  • 混淆 WebGL 则要更改 WebGLbufferData getParameter方法等等。
  • 混淆Canvas 指纹则需要更改 toDataURL 方法,比如 先使用 toDataURL() 将整个canvas的内容导出,通过 getImageData() 复制画布上指定矩形的像素数据并修改然后通过 putImageData() 将图像数据放回,然后再使用 toDataURL() 导出的图片,完成混淆。