覆盖 CanvasRenderingContext2D.getImageData()

Overriding CanvasRenderingContext2D.getImageData()

我正在尝试覆盖内置方法 CanvasRenderingContext2D.getImageData()。我想覆盖实现,以便修改后的函数使用 canvas 上下文来修改 canvas,然后调用原始函数,如果函数未被覆盖,该函数应该 return 不同的数据.我这样做的原因是为了防止浏览器指纹识别。

canvas.js

(function(){
    'use strict';

    var originalGetImageData = CanvasRenderingContext2D.prototype.getImageData;

    // This function just adds 1 to each RGBA component in the array for testing.
    // Will add random values for the real thing.
    function randomiseImageData(image) {        
        var imageData = image.data;

        var imageLength = imageData.length;

        for (var i = 0; i < imageLength; i++) {
            imageData[i] += 1;
        }

        var modifiedImage = new ImageData(image.width, image.height);

        return modifiedImage;
    }

    CanvasRenderingContext2D.prototype.getImageData = function(sx, sy, sw, sh) {
        console.log("[ALERT] " + window.location.hostname + " called CanvasRenderingContext2D.getImageData()");

        const origin = window.location.hostname;

        Math.seedrandom(origin);

        var image = originalGetImageData.call(this, sx, sy, sw, sh);

        return randomiseImageData(image);
    };
})();

您正在 return 创建一个新的 ImageData 对象。

我猜你想要的是 return 填充的。
由于您已经修改了 data 数组,您可以简单地 return 原始 ImageData,您的修改将已经完成。

// shortened version
(function(){
 const ori = CanvasRenderingContext2D.prototype.getImageData;
 CanvasRenderingContext2D.prototype.getImageData = function(){
  let imageData = ori.apply(this, arguments);
  // modify the Uint8Array
  imageData.data.forEach((v, i, a) => a[i]+=1);
  // return the now modified ImageData
  return imageData;
  };
 })()
 
var ctx = document.createElement('canvas').getContext('2d');
console.log(ctx.getImageData(0,0,1,1));

如果你真的想创建一个新的ImageData,那就是

new ImageData(imageData, image.width, image.height);
//             ^^
//           pass the data to fill the new ImageData object

但请注意,浏览器支持不是很好,这样做您不会有任何收获。

您无法删除指纹。

更多The Web never forgets

您无法绕过指纹识别。你能做的最好的事情是 return 最常见的指纹(不容易确定)增加你可能属于的设备集。

返回一组随机像素(或将每个像素通道递增一个)如果您是唯一一个这样做的人,那将是您能做的最糟糕的事情。它绝对会将您的浏览器标记为唯一,并让追踪软件知道 return 更改数据的浏览器只是其中一个,或者是极少数浏览器中的一个。

阻止指纹识别的最佳方法是通过一种通用且广泛采用的数据 return 策略。如果每个浏览器 return 全部归零(透明黑色),那么将没有唯一性,因此无法根据 canvas.

跟踪设备

Canvas 指纹只是指纹的一部分,还有更多的数据来源可以帮助识别设备。浏览器、浏览器版本、OS、OS 版本、屏幕分辨率和一长串其他内容。即使您消除了 canvas 作为唯一性来源,它也毫无意义,除非您对其余信息执行相同的操作。

缓解

所以说 return 归零数据的代码如下。

(function () {
    if (window.CanvasRenderingContext2D) {
        const gid = CanvasRenderingContext2D.prototype.getImageData;
        CanvasRenderingContext2D.prototype.getImageData = function (x, y, w, h) {
            var data = gid.bind(this)(x, y, w, h);
            data.data.fill(0);  // fill with zero
            return data;
        }
        // Token way to avoid JS from finding out that you have overwritten the prototype overwrite
        // the toString method as well (note ctx.getImageData.toString.toString() will
        // still show you have changed the prototype but over writing Object.toSting is not worth the problems)
        CanvasRenderingContext2D.prototype.getImageData.toString = function () {
            return "function getImageData() { [native code] }";
        }
    }
}());