Recently, I've come across an interesting canvas fingerprinting evasion method designed to trick browsers into generating distinct fingerprints by deliberately adding redundant or seemingly "useless" canvas drawing operations. Essentially, the method works by repeatedly resetting the canvas width and redrawing very similar content, adding subtle variations to confuse fingerprinting algorithms.
Here are two JavaScript code snippets illustrating this approach:
Snippet 1:
;(async function(){
var canvas = document.createElement("canvas");
canvas.width = 96;
canvas.height = 96;
var context = canvas.getContext('2d', {willReadFrequently: true});
context.font = "94px sans-serif";
context.fillStyle = "#000";
context.fillText("π¨βπ",-1.0,96.0);
canvas.width = canvas.width;
canvas.width = canvas.width;
context.font = "94px sans-serif";
context.fillStyle = "#000";
context.fillText("π¨βπ",-1.0,96.0);
canvas.width = canvas.width;
canvas.width = canvas.width;
context.font = "94px sans-serif";
context.fillStyle = "#000";
context.fillText("π΅",-1.0,96.0);
var str = context.getImageData(0,0,96,96).data;
// Convert pixel data to hex fingerprint...
})();
Snippet 2 (variation):
;(async function(){
var canvas = document.createElement("canvas");
canvas.width = 96;
canvas.height = 96;
var context = canvas.getContext('2d', {willReadFrequently: true});
context.font = "94px sans-serif";
context.fillStyle = "#000";
context.fillText("π¨βπ",-1.0,96.0);
canvas.width = canvas.width;
canvas.width = canvas.width;
context.font = "94px sans-serif";
context.fillStyle = "#000";
context.fillText("π¨βπ",-1.0,96.0);
canvas.width = canvas.width;
canvas.width = canvas.width;
context.font = "94px sans-serif";
context.fillStyle = "#000";
context.fillText("π΅",-1.0,96.0);
canvas.width = canvas.width;
canvas.width = canvas.width;
context.font = "94px sans-serif";
context.fillStyle = "#000";
context.fillText("π΅β",-1.0,96.0); // subtle addition
var str = context.getImageData(0,0,96,96).data;
// Convert pixel data to hex fingerprint...
})();
This technique leverages small differences in the final canvas renderingβparticularly using emojis and invisible Unicode modificationsβto create distinct fingerprints, effectively confusing fingerprint trackers. By repeatedly clearing the canvas (e.g., canvas.width = canvas.width), browsers often register each redrawing as a different canvas output, thus complicating attempts at reliably fingerprinting.
So with Perfect Canvas, we only get precomputed canvas request, then how are you gonna deal with this ?