​HTML5 Canvas指纹及反追踪介绍​

1 Canvas指纹的简介

很多网站通过Canvas指纹来跟踪用户。browserleaks[1]是一个在线检测canvas指纹的网站。一般的指纹实现原理即通过canvas画布绘制一些图形,填写一些文字,随后获取图形的base64编码,再经过hash后得到最终的指纹。

下面是一个简单的例子,最终调用toDataUrl会生成一段编码。

var getCanvasFp = function (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

}


2 防止被追踪

通过1的介绍,为了防止被追踪,需要对生成进行调整,这样避免暴露自己的数据。变化的形式有很多,例如变换颜色,透明度及字体。此外canvas提供了getImageData[2]得到一个图形的像素数据,对这里的数据进行变化也能对结果进行变化。这里以anticanvas[3]为例,其它的处理方法原理类似。 下面的样例代码原理很简单,就是通过对原始方法进行拦截来对当前结果改变。在编码中通过闭包[4]保存原始方法。最终实现代理的效果,原始方法调用前对参数进行修改。

(function(){

CanvasRenderingContext2D.prototype.getImageData = function(a) {

return function() {

console.log("Context");

spoofFromContext(this,a);

return a.apply(this, arguments);

};

}(CanvasRenderingContext2D.prototype.getImageData);

HTMLCanvasElement.prototype.toDataURL=(function(){

var original = HTMLCanvasElement.prototype.toDataURL;

return function() {

spoof(this);

return original.apply(this,arguments);

};

})();

function spoof(canvas){

var ctx = canvas.getContext("2d");

spoofFromContext(ctx);

}

function spoofFromContext(ctx,a){

if(!a) a = ctx.getImageData;

var data = a.call(ctx,0, 0, ctx.canvas.width, ctx.canvas.height);

for(var c=0; c<data.data.length; c=c+4){

var r = data.data[c];

var g = data.data[c+1];

var b = data.data[c+2];

var a = data.data[c+3];

if(a!=0){

data.data[c]=r-Math.random();

data.data[c+1]=g-Math.random();

data.data[c+2]=b-Math.random();

data.data[c+3]=a-Math.random();

}

}

ctx.putImageData(data, 0, 0);

console.log("Spoofed");

}

})();