January 2019

Canvas Fingerprinting


Fingerprint:

Canvas Fingerprinting is fully explained by this paper.

The first step to implementing a basic canvas fingerprint is to have a canvas element on the page. For image extraction, it is not required that the canvas already exist or is even visible on the page, the call to canvas.toDataUrl() can be done on an "invisible" canvas element.

Then we need to fill the canvas element with some unique text to force as much uniqueness to be exercised by the OS, GPU, and browser engine. Specifically the text: "How quickly daft jumping zebras vex. (Also, punctuation: Ɛ/ζ}Φ)" and with 18pt font Arial just as the paper mentioned.

// get the 2d context and apply the text
const context = canvas.getContext("2d");
context.font = "12pt Arial";
context.textBaseline = "top";
context.fillText("How quickly daft jumping zebras vex. (Also, punctuation: Ɛ/ζ}Φ)", 2, 2);

Next we need to fetch the contents of the image in Base64.

// fetch the base64 encoded image
const fingerprint = canvas.toDataURL("image/png");

Output:

Now if we take that large Base64 output and run it through a fast hash algorithm (such as Murmur Hash), we can create a more reasonable hash to utilize for fingerprinting.

Fingerprint Output:

Hidden Canvas Variant

This version shows how you can do the same methods as above without a canvas element originally on the page. It also shows how no notifications are made to the user about the canvas element on insertion to the page, or when information is extracted from the element. Now as mentioned in the paper, there are valid uses to allow these operations normally (image editors, drawing applications, etc.) but browsers have the capability to notify users when certain actions are attempting to occur.

// create the canvas
const canvas= document.createElement("canvas");
canvas.id = "c3";
canvas.style.display = "none";
canvas.height = 30;
canvas.width = 700;
canvas.style.background = "white";

// insert it into the dom
document.getElementById("b").append(canvas);

// get the 2d context and apply the text
const context = canvas.getContext("2d");
context.font = "18pt Arial";
context.textBaseline = "top";
context.fillText("How quickly daft jumping zebras vex. (Also, punctuation: Ɛ/ζ}Φ)", 2, 2);

// fetch the base64 encoded image
const fingerprint = canvas.toDataURL("image/png");
const murmur = Murmur.x64hash128(fingerprint, 42);
console.log(fingerprint);

// insert into page element
const elem = document.getElementById("f2");
elem.innerHTML = murmur;

Fingerprint:

Devin Carr / Home / Posts