前言
从前有座山,山里有座庙,庙里有个老和尚,长得真是俏,俏也不争春,只把春来报,待到山花烂漫时,他在丛中笑。
一.浏览器指纹
1.什么是浏览器指纹
浏览器指纹我们可以理解成是一个用户设备的浏览器的唯一id(有点类似手机设备的IMEI),通过浏览器指纹,我们可以做一些埋点操作或者是鉴权,
辨别是否是同一用户,其是否有更换设备(更换浏览器)等。
2.帆布指纹
帆布指纹原理(浏览器+操作系统+GPU+图形驱动器的唯一性) HTML<canvas>元素用于通过 JavaScript 动态绘制图形,将这个canvas进行base64编码,最后生成base64编码的hash值, hash值就是canvas指纹。括号中的话属于引用 (浏览器+操作系统+GPU+图形驱动器的唯一性,任何两个不相同的浏览器,或者不同电脑上面的相同浏览器 都会存在细丝末毫的不同,而这个不同会导致浏览器绘制的图像肉眼看起来相同,但是某几个像素点可能存在几个像素的偏移或者灰度偏移, 然后生成的hash值就完全不一样了)。话虽然看起来对好像是这么回事,但是我们是在浏览器上执行的js代码,如果在中层做一点手脚是不是直接就可以干扰生成的指纹, 或者在浏览器这层屏蔽掉更底层的差异,这样生成的指纹会不会导致不同的设备之间有着相同的指纹。
3.fingerprintjs2
3.1.什么是fingerprintjs2
什么是fingerprintjs2(音频指纹+显示器指纹+摄像指纹+语言+useragent+.............多重方式一混合) fingerprintjs2是通过设备浏览器信息获取浏览器指纹的插件(官方宣称其识别精度达到99.5%),详细了解可查看fingerprintjs2官方文档 github地址:https://github.com/Valve/fingerprintjs2
3.2.fingerprint2会取的设备信息
userAgent: navigator.userAgent本地信息
language : 语言
colorDepth: 返回目标设备或缓冲器上的调色板的比特深度 screen.colorDepth
deviceMemory: 以千兆字节为单位返回设备内存量。该值是通过舍入到最接近的2的幂并将该数除以1024而给出的近似值。链接
pixelRatio: 像素比 devicePixelRatio
hardwareConcurrency:navigator.hardwareConcurrency返回可用于运行在用户的计算机上的线程的逻辑处理器的数量
screenResolution: 检测屏幕宽高,并根据屏幕方向矫正返回值[width,height]
availableScreenResolution:返回屏幕分辨率[width,height],无头浏览器无法获取
timezoneOffset: 返回从当前区域设置(主机系统设置)到UTC的时区差异(以分钟为单位)链接
timezone:时区
sessionStorage: 是否支持sessionStorage,不支持时返回错误
localStorage: 是否支持localStorage
indexedDb:是否支持indexedDb
addBehavior:此时可能未定义body或以编程方式删除
openDatabase: 返回是否支持Web SQL
cpuClass:返回浏览器系统的 CPU 等级,一般无法获取
platform: 返回表示浏览器平台的字符串,该规范允许浏览器始终返回空字符串,因此不要依赖此属性来获得可靠的答案.链接
doNotTrack: 返回用户的“不跟踪”设置。如果用户请求不被网站,内容或广告跟踪,则为“1”。一般结果为。
plugins:返回浏览器安装的插件列表
canvas: 如果浏览器支持canvas则返回生成baes64数据
webgl:返回浏览器对webgl绘图协议的支持情况汇总
webglVendorAndRenderer: 返会显卡型号相关信息
adBlock:返回是否安装去广告插件。
hasLiedLanguages: 返回用户是否改变了首选语言
hasLiedResolution:返回用户是否改变了分辨率
hasLiedOs:返回用户是否改变了操作系统
hasLiedBrowser:返回用户是否改变了浏览器
touchSupport: 返回最大触摸点数,是否支持touch,是否支持ontouchstart事件]
fonts:返回从64种字体种筛选出的可用字体
fontsFlash:Flash字体枚举,如果没有swfobject,不会触发。
audio: 返回音频指纹
enumerateDevices:navigator.mediaDevices 请求可用媒体输入和输出设备的列表,例如麦克风,相机,耳机等
3.3.工作逻辑
取到以上值后[数组],将数组转为值字符串
将取到的字符串做为key 传入x64hash128方法,生成指纹
二.代码实现
1.帆布指纹Canvas实现
①引入相应js(非必要)
<script src=”http://pv.sohu.com/cityjson?ie=utf-8″></script>
<script src=”https://cdn.bootcss.com/blueimp-md5/2.10.0/js/md5.min.js”></script>
②js代码
/**
* https://jsbin.com/qisodaz/edit?js,output
*
* @param options
* @returns {*[]}
*/
var getCanvasFp = function (options) {
options = options ? options : {};
var result = []
// Very simple now, need to make it more complex (geo shapes etc)
var canvas = document.createElement(‘canvas’)
canvas.width = 2000
canvas.height = 200
canvas.style.display = ‘inline’
var ctx = canvas.getContext(‘2d’)
// detect browser support of canvas winding
// http://blogs.adobe.com/webplatform/2013/01/30/winding-rules-in-canvas/
// https://github.com/Modernizr/Modernizr/blob/master/feature-detects/canvas/winding.js
ctx.rect(0, 0, 10, 10)
ctx.rect(2, 2, 6, 6)
result.push(‘canvas winding:’ + ((ctx.isPointInPath(5, 5, ‘evenodd’) === false) ? ‘yes’ : ‘no’))ctx.textBaseline = ‘alphabetic’
ctx.fillStyle = ‘#f60’
ctx.fillRect(125, 1, 62, 20)
ctx.fillStyle = ‘#069′
// https://github.com/Valve/fingerprintjs2/issues/66
if (options.dontUseFakeFontInCanvas) {
ctx.font = ’11pt Arial’
} else {
ctx.font = ’11pt no-real-font-123′
}
ctx.fillText(‘Cwm fjordbank glyphs vext quiz, \ud83d\ude03’, 2, 15)
ctx.fillStyle = ‘rgba(102, 204, 0, 0.2)’
ctx.font = ’18pt Arial’
ctx.fillText(‘Cwm fjordbank glyphs vext quiz, \ud83d\ude03’, 4, 45)// canvas blending
// http://blogs.adobe.com/webplatform/2013/01/28/blending-features-in-canvas/
// http://jsfiddle.net/NDYV8/16/
ctx.globalCompositeOperation = ‘multiply’
ctx.fillStyle = ‘rgb(255,0,255)’
ctx.beginPath()
ctx.arc(50, 50, 50, 0, Math.PI * 2, true)
ctx.closePath()
ctx.fill()
ctx.fillStyle = ‘rgb(0,255,255)’
ctx.beginPath()
ctx.arc(100, 50, 50, 0, Math.PI * 2, true)
ctx.closePath()
ctx.fill()
ctx.fillStyle = ‘rgb(255,255,0)’
ctx.beginPath()
ctx.arc(75, 100, 50, 0, Math.PI * 2, true)
ctx.closePath()
ctx.fill()
ctx.fillStyle = ‘rgb(255,0,255)’
// canvas winding
// http://blogs.adobe.com/webplatform/2013/01/30/winding-rules-in-canvas/
// http://jsfiddle.net/NDYV8/19/
ctx.arc(75, 75, 75, 0, Math.PI * 2, true)
ctx.arc(75, 75, 25, 0, Math.PI * 2, true)
ctx.fill(‘evenodd’)if (canvas.toDataURL) { result.push(‘canvas fp:’ + canvas.toDataURL()) }
return result
}let fingerPrintRawData = getCanvasFp()[1];
console.log(“<br>浏览器指纹–Canvas : ” + fingerPrintHash);
2.fingerprintjs2实现
①引入对应js
<script src="../static/fingerprint2.min.js"></script>
②js代码
let options = {
excludes: {
// navigator.userAgent
userAgent: true,
// 语言
language: true,
// 返回目标设备或缓冲器上的调色板的比特深度
colorDepth: true,
// 以千兆字节为单位返回设备内存量。该值是通过舍入到最接近的2的幂并将该数除以1024而给出的近似值
deviceMemory: true,
// 像素比
pixelRatio: true,
// 返回可用于运行在用户的计算机上的线程的逻辑处理器的数量
hardwareConcurrency: true,
// 检测屏幕宽高,并根据屏幕方向矫正返回值[width,height]
screenResolution: false,
// 返回屏幕分辨率[width,height],无头浏览器无法获取
availableScreenResolution: true,
// 返回从当前区域设置(主机系统设置)到UTC的时区差异(以分钟为单位)
timezoneOffset: true,
// 时区
timezone: true,
// 是否支持sessionStorage,不支持时返回错误
sessionStorage: true,
// 是否支持localStorage
localStorage: true,
// 是否支持indexedDb
indexedDb: true,
// 此时可能未定义body或以编程方式删除
addBehavior: true,
// 返回是否支持Web SQL
openDatabase: true,
// 返回浏览器系统的 CPU 等级,一般无法获取
cpuClass: true,
// 返回表示浏览器平台的字符串,该规范允许浏览器始终返回空字符串,因此不要依赖此属性来获得可靠的答案
platform: false,
// 返回用户的“不跟踪”设置。如果用户请求不被网站,内容或广告跟踪,则为“1”。一般结果为*
doNotTrack: true,
// 返回浏览器安装的插件列表
plugins: true,
// 如果浏览器支持canvas则返回生成baes64数据
canvas: true,
// 返回浏览器对webgl绘图协议的支持情况汇总
webgl: false,
// 返会显卡型号相关信息
webglVendorAndRenderer: true,
// 返回是否安装去广告插件
adBlock: true,
// 返回用户是否改变了首选语言
hasLiedLanguages: true,
// 返回用户是否改变了分辨率
hasLiedResolution: true,
// 返回用户是否改变了操作系统
hasLiedOs: true,
// 返回用户是否改变了浏览器
hasLiedBrowser: true,
// 返回最大触摸点数,是否支持touch,是否支持ontouchstart事件
touchSupport: true,
// 返回从64种字体种筛选出的可用字体
fonts: false,
// Flash字体枚举,如果没有swfobject,不会触发
fontsFlash: false,
// 返回音频指纹
audio: false,
// 请求可用媒体输入和输出设备的列表,例如麦克风,相机,耳机等
enumerateDevices: true,
},
};function getFingerprint(){
let fingerprint;
if (window.requestIdleCallback) {
requestIdleCallback(function () {
Fingerprint2.get(options, function (components) {
const values = components.map(function(component, index) {
return component.value
})
const murmur = Fingerprint2.x64hash128(values.join(”), 31);
console.log(“浏览器指纹–Finger : ” + murmur);
fingerprint = murmur;
let fingerPrintRawData = getCanvasFp()[1];
let fingerPrintHash = md5(fingerPrintRawData);
console.log(“<br>浏览器指纹–Canvas : ” + fingerPrintHash);
document.getElementById(“fingerPrint”).value = fingerprint;
document.getElementById(“canvas”).value = fingerPrintHash;
})
})
} else {
setTimeout(function () {
Fingerprint2.get(options, function (components) {
const values = components.map(function(component, index) {
return component.value
})
const murmur = Fingerprint2.x64hash128(values.join(”), 31);
fingerprint = murmur;
console.log(“浏览器指纹–Finger : ” + murmur);
let fingerPrintRawData = getCanvasFp()[1];
let fingerPrintHash = md5(fingerPrintRawData);
console.log(“<br>浏览器指纹–Canvas : ” + fingerPrintHash);
document.getElementById(“fingerPrint”).value = fingerprint;
document.getElementById(“canvas”).value = fingerPrintHash;
})
}, 500)
}
setTimeout(function () {
console.log(“浏览器指纹 : ” + fingerprint);
}, 600);
}
结语
“朝暮与年岁共往,然后与你一同行至天光”。——河唐先生《天光》