HTML5 Canvas Fingerprinting

This browser does not support HTML5 Canvas

This is a simple Proof-of-Concept that Browser Fingerprinting is possible without any of User-Agent identifiers.

The method is based on the fact that the same HTML5 Canvas element can produce exceptional pixels on a different web browsers, depending on the system on which it was executed.

This happens for several reasons: at the image format level — web browsers uses different image processing engines, export options, compression level, final images may got different hashes even if they are pixel-perfect; at the pixmap level — operating systems use different algorithms and settings for anti-aliasing and sub-pixel rendering. We don't know all the reasons, but we have already collected more than a thousand unique signatures.

Well, let's begin. After the tables we give a brief explanation…

Canvas (basic support)× False
Text API for Canvas× False
Total Visitors Processed
Total Sets (User-Agents)
Total Groups (Signatures)
Found in DB
General ConclusionIt is very likely that you are using Web Browser on Operating System Your system fingerprint appears to be unique, yet we don't collect signatures here, just check.
Sets in this Group

Scope of the Issue

Unlike the other «browser detection» tricks, it deals with many OS features related on graphics environment. Potentially it can be used to identify the video adapter, especially if you are use WebGL profiling, not just Canvas 2D Context. By the way different graphics card drivers can also sometimes affect to regular fonts rendering.

This technique is good to make a unique/trackable signature when it is combined with other common methods, e.g. in systems such as EFF's Panopticlick or PET's Fingerprinting.

Much more difficult to obtain specific parameters of the system. It is signature-based, and the problem of signature collection is the main limiting factor why it is not so easy at the moment. We can produce Canvas/WebGL pixmap, and associate its fingerprint with own machine, but we cannot ask each visitor individually about his system and hardware. Certainly, there are ways to get such info: malware, social engineering, mturk, but for now we do not consider them, for now :)

How Does It Work

First of all, there will be no any ready-to-embed solutions, this is only demo. We are just going to share some snippets how it works under the hood.

Our signatures DB is based on a simple association between «Canvas Fingerprint» and «HTTP User-Agent», so it is very prone to false positives because of the faked headers, etc. If your fingerprint matches one that is stored in database, the program will show you which User-Agents have the same signature. Otherwise, well, your browser seems unique for our modest DB, but we cannot sign you here in any way, because we do not collect signatures from this website.

Here is the javascript code that produce the pixels:

  1. // text with lowercase/uppercase/punctuation symbols
  2. var txt = "BrowserLeaks,com <canvas> 1.0";
  3. ctx.textBaseline = "top";
  4. // the most common type
  5. ctx.font = "14px 'Arial'";
  6. ctx.textBaseline = "alphabetic";
  7. ctx.fillStyle = "#f60";
  8. ctx.fillRect(125,1,62,20);
  9. // some tricks for color mixing
  10. ctx.fillStyle = "#069";
  11. ctx.fillText(txt, 2, 15);
  12. ctx.fillStyle = "rgba(102, 204, 0, 0.7)";
  13. ctx.fillText(txt, 4, 17);
  14. // more explanation? see the Further Reading below...
The result will be the image

×Your browser does not support HTML5 Canvas

To create a signature from the canvas, we must export the pixels from the application's memory using the toDataURL() method, which will return the base64-encoded string of the binary image file. Then we can just create MD5 hash of this string.

But for the PoC we came up with a slightly more interesting decision. As we know, PNG files is divided into chunks, and last part of each chunk is a 32-bit CRC checksum calculated on the preceding bytes. So all we need is to extract the IDAT CRC — that will be the browser fingerprint:

  1. // code is simple but we must provide some conversion functions
  2. // it is still less and faster than md5
  3. function bin2hex (s) {
  4. // ... bin-to-hex conversion code ...
  5. }
  6. if (typeof window.atob == "undefined") {
  7. function atob(a) {
  8. // IE9 still has not atob() function (base64-to-binary)
  9. // ... you must put some replacement here ...
  10. }
  11. }
  12. var b64 = canvas.toDataURL.replace("data:image/png;base64,","");
  13. var bin = atob(b64);
  14. // crc32 takes only 4 bytes and placed from 16 to 12 byte from the end of file
  15. var crc = bin2hex(bin.slice(-16,-12));