什么是浏览器指纹
浏览器指纹是通过收集和识别网页浏览器特征的一种技术。它基于用户使用浏览器时产生的各种信息(例如操作系统、浏览器版本、插件、屏幕分辨率、系统语言等),用于唯一标识和区分不同的设备或用户。简单而言,浏览器指纹能够实现在用户未登陆或者使用无痕模式的时候,依旧能够准确识别出该用户。
场景:用户第一天未登录的情况下浏览了页面 A,隔天用户使用同个浏览器并且使用了无痕模式再次浏览了页面 A,此时我们的页面能够依据浏览器指纹,识别出两次不带登陆态的用户是同一个人,从而实现一些精准推送。
原理及实现?
信息熵(entropy)是接收的每条消息中包含的信息的平均量,熵越高,则能传输越多的信息,熵越低,则意味着传输的信息越少。浏览器指纹是由许多浏览器的特征信息综合起来的,其中特征值的信息熵也是不尽相同
浏览器指纹一般而言,能分成几大类:
-
浏览器基本指纹
比如硬件类型(Apple)、操作系统(Mac OS)、用户代理(User agent)、系统字体、语言、屏幕分辨率、浏览器插件 (Flash, Silverlight, Java, etc)、浏览器扩展、浏览器设置 (Do-Not-Track, etc)、时区差(Browser GMT Offset)等众多信息,这些指纹信息“类似”人类的身高、年龄等,有很大的冲突概率,只能作为辅助识别
-
Canvas 指纹
使用 Canvas 绘制相同的元素,但是由于不同的浏览器所处的系统的差别,字体渲染引擎不同,对抗锯齿、次像素渲染等算法也不同,通过将基于 Canvas 绘制的元素转成图片编码,不同的浏览器就可以得到不同的结果,通过对这个不同结果进行哈希,就能拿到浏览器的 Canvas 指纹
import sha256 from 'crypto-js/sha256'; const outScreenCanvas = document.createElement('canvas') const canvasImageData = outScreenCanvas.toDataURL(); const canvasFinger = sha256(canvasImageData); -
音频指纹
与 Canvas 指纹非常类似,其原理也是基于硬件设备或者软件的差别,来产生不同的音频输出,然后计算得到不同的 hash 来作为音频指纹。
方法一:生成音频信息流(三角波),对其进行 FFT 变换,计算 SHA 值作为指纹。
方法二:生成音频信息流(正弦波),进行动态压缩处理,计算 MD5 值。
两种方法都是在音频输出到音频设备之前进行清除,用户根本就毫无察觉就被获取了指纹。以 fingerprintjs2 的音频指纹源码为例:
var each = function(obj, iterator) { if (Array.prototype.forEach && obj.forEach === Array.prototype.forEach) { obj.forEach(iterator) } else if (obj.length === +obj.length) { for (var i = 0, l = obj.length; i < l; i++) { iterator(obj[i], i, obj) } } else { for (var key in obj) { if (obj.hasOwnProperty(key)) { iterator(obj[key], key, obj) } } } } var AudioContext = window.OfflineAudioContext || window.webkitOfflineAudioContext var context = new AudioContext(1, 44100, 44100) var oscillator = context.createOscillator() oscillator.type = 'triangle' oscillator.frequency.setValueAtTime(10000, context.currentTime) var compressor = context.createDynamicsCompressor() each([ ['threshold', -50], ['knee', 40], ['ratio', 12], ['reduction', -20], ['attack', 0], ['release', 0.25] ], function (item) { if (compressor[item[0]] !== undefined && typeof compressor[item[0]].setValueAtTime === 'function') { compressor[item[0]].setValueAtTime(item[1], context.currentTime) } }) oscillator.connect(compressor) compressor.connect(context.destination) oscillator.start(0) context.startRendering() var audioTimeoutId = setTimeout(function () { console.warn('Audio fingerprint timed out. Please report bug at https://github.com/Valve/fingerprintjs2 with your user agent: "' + navigator.userAgent + '".') context.oncomplete = function () { } context = null return done('audioTimeout') }, 100) context.oncomplete = (event) => { try { clearTimeout(audioTimeoutId) audioFingerprint.value = event.renderedBuffer.getChannelData(0).slice(4500, 5000) .reduce(function (acc, val) { return acc + Math.abs(val) }, 0) .toString() oscillator.disconnect() compressor.disconnect() audioFingerprintHash.value = sha256(audioFingerprint.value) } catch (error) { console.log(error) return } }
使用场景
-
针对性的广告推送
在网站上浏览某个商品,了解了相关的商品信息,但并没有下单购买,甚至没有进行登录操作。过两天用同台电脑访问其他网站的时候却发现很多同类商品的广告。
-
协助识别同一设备、打击黑产
在某博客中你有多个小号(水军),这些小号的存在就是为了刷某个帖子的热度或者进行舆论引导,即便你在切换账号的时候清空了 cookie、本地缓存,重开路由器甚至使用 vpn 来进行操作,但是管理人员可能还是知道这是同一个人在操作,从而被打击。
如何防范
-
禁用 JS
这是一个比较暴力的方法,直接禁止网站使用 JavaScript 可以非常有效地防御浏览器指纹追踪,但是这样会导致页面较大部分地功能不可用。
而且非常不幸的是,即便禁止了 JS 但是还可以通过 CSS 来采取浏览器的信息@media(device-width: 1080px) { body { background: url("https://example.com/1080.png") } }可以在服务器中看 1080.png 图片的请求日志,就可以得知哪些用户的屏幕是 1080px 的
所以可以看到,浏览器指纹,只要网站想,其实浏览器指纹是非常难以防范的。
评论区