# Canvas blocker extensions and your fingerprint

> A canvas blocker extension hides your canvas fingerprint by rewriting methods in page JavaScript, and a fresh iframe reads the rewrite: 33 leaked on one probe.

Source: https://mosaicbrowser.com/blog/canvas-blocker-extensions

## Why canvas blocker extensions can make you easier to spot

A canvas blocker extension hides your canvas fingerprint by rewriting methods in page JavaScript, and a fresh iframe reads the rewrite: 33 leaked on one probe.

![33: 33 patched functions, one four-line probe, beside a screenshot: a Mosaic leak test: every surface a site reads, checked from inside the profile page](https://mosaicbrowser.com/covers/canvas-blocker-extensions.webp)

A canvas blocker extension changes `toDataURL`, `toBlob` and `getImageData` by replacing them in the page's JavaScript, so a site gets a blank image, an error, or pixels with noise in them. That hides your real canvas hash. It also leaves a replaced browser function where a native one should be, and a page can find that in four lines. You swap a common signal for a rare one.

## What a canvas blocker extension does to the page

An extension cannot reach the code that draws a canvas. That code is C++ inside the browser. What it can reach is the page's JavaScript world, so it runs a script before the site's own and replaces the methods that read pixels back. Extensions such as CanvasBlocker offer a choice of modes: refuse the read, return a fake, or add a little random noise to the real pixels.

Each mode changes the value a site reads. None of them changes the fact that the method is no longer the one Chrome shipped, and fingerprinting scripts have long since stopped asking only for the value.

## The iframe probe we ran against our own payload

We know how well this kind of layer hides because we wrote one. A JavaScript payload of ours replaced getters the same way an extension does, and it rewrote `Function.prototype.toString` so each replaced function would print `[native code]` when asked for its source. Inside the page it worked: our patched `toString` looked each function up in a private table and answered with the native string.

A page gets a second JavaScript world for free, though. Any iframe has its own copy of every built-in, including its own `toString`, and our table was not in it:

```
const f = document.createElement('iframe')
document.documentElement.appendChild(f)
f.contentWindow.Function.prototype.toString.call(
  Object.getOwnPropertyDescriptor(Navigator.prototype, 'userAgent').get)

```

The iframe's `toString` had never heard of our function, so it printed the real thing: our getter's JavaScript source. Measured on one profile, **33** patched functions across Navigator, Screen, canvas, WebGL, audio and client rects gave up their bodies to that probe. Canvas was not special. Every method the layer touched could be read the same way.

Mosaic is a desktop app

Email yourself the link and open it on your computer.

[How it works: Machines that exist](https://mosaicbrowser.com/features/real-fingerprints)

## Why fixing the toString leak is not the end

We fixed that one. Each patch became a JavaScript `Proxy` wrapped around the real native function, which V8 prints as native code in every frame. That bought us agreement between the page and the iframe, and it found the next edge on its own.

`Function.prototype.toString` itself could not be wrapped that way. Installed as a proxy over the function it replaced, it recursed until the stack ran out on deviceandbrowserinfo.com's bot check: four `Maximum call stack size exceeded` errors, their detection script dead, and no verdict on the page. We had logged that row as "unread" for months, assuming the site was slow. One run in a stock Chrome with only our script added reproduced it, and the untouched control rendered its verdict in seconds.

That is the work an extension author is signing up for: every method, in every frame, in every worker, with every error thrown the way the native one throws it. CreepJS lists each place a replacement behaves unlike the native as a "lie", and [what a CreepJS lie is](https://mosaicbrowser.com/blog/what-a-creepjs-lie-is) walks through the checks it runs.

## Noise has a second tell: read it twice

Suppose the extension's replacement were perfect. The noise itself can still be caught.

A real browser that draws the same shape twice gives the same pixels twice. A blocker that adds fresh random noise to every read gives two different hashes for one drawing on one page, and a script needs only to read twice and compare. Noise tied to each pixel's position fails a different test: draw one shape at two offsets, and the difference between the copies should be zero bytes. Ours once gave 76. The [four-line offset probe](https://mosaicbrowser.com/blog/canvas-offset-probe) has the code and the numbers.

Blocking outright is louder still. A canvas that comes back blank, or a read that throws, is something almost no ordinary browser produces, so it marks the browser as unusual on the first call.

## What the real canvas hash was telling the site

The hash a blocker hides is less personal than it sounds. A canvas readback depends on the GPU, the driver, the fonts and how the operating system rasterises text, so it names a kind of machine that many people share. [What a canvas hash reveals](https://mosaicbrowser.com/blog/what-a-canvas-hash-reveals) covers which parts of the stack move it.

So the trade is a hash shared by everyone with your kind of laptop, against a modified browser function that very few visitors carry. The second is easier to pick out of a crowd than the first. You can see what your own browser gives away on our [canvas fingerprint](https://mosaicbrowser.com/tools/canvas-fingerprint) page, and run it once with the extension and once without.

## Where we put the canvas change instead

On Mosaic's own engine, Tesseract Chrome, nothing is spoofed with injected JavaScript. Canvas output is changed in C++, where the pixels are stored, so `toDataURL` and `toBlob` remain Chrome's own native methods and the page, its iframes and its workers read the same result. The probe above has nothing to catch, because no function was replaced.

That is not a promise that no site can tell the profile apart. It means the canvas value belongs to a machine that exists, drawn together with its GPU and fonts, which is what [real fingerprints](https://mosaicbrowser.com/features/real-fingerprints) describes. In an ordinary Chrome, the choice a blocker offers is a common hash or a rare modification, and the probe above is how a site tells which one you picked.

## Questions

### Does a canvas blocker extension stop canvas fingerprinting?

It stops a site reading your real canvas hash, which names a kind of machine. It usually adds a new signal in exchange: a rewritten browser function, which a page can find with a few lines of JavaScript.

### How can a site tell a canvas blocker is installed?

By asking a function from a fresh iframe to print the source of the canvas method. A native method prints [native code]; a replaced one can print its own body. Reading the same canvas twice and getting two different hashes gives it away too.

### Is blocking canvas better than adding noise?

Neither looks like a normal browser. A blank or failing canvas is rare, and per-read noise produces hashes that change between two reads of the same drawing, which no unmodified Chrome does.

### How does Mosaic handle canvas fingerprints?

On its own engine, Tesseract Chrome, the canvas change is made in C++ inside the browser, so the method a page inspects is still Chrome's native one, in every frame and worker.

## Related

[What a CreepJS lie is, and why stock Chrome has none](https://mosaicbrowser.com/blog/what-a-creepjs-lie-is): CreepJS lies detected means an API behaved unlike the native one: wrong toString, wrong descriptor, or a worker that disagrees. Stock Chrome has none.

[The four-line probe that catches position-keyed canvas noise](https://mosaicbrowser.com/blog/canvas-offset-probe): To detect canvas noise injection, draw one shape twice at two offsets and diff them. A real browser gives 0 bytes; our old noise gave 76.

[What a canvas hash actually tells a site about your machine](https://mosaicbrowser.com/blog/what-a-canvas-hash-reveals): A canvas fingerprint reveals your rendering stack: GPU, driver, fonts, the OS's text rasteriser and antialiasing. It names a kind of machine, not a person.

[Machines that exist: fingerprints drawn from real hardware](https://mosaicbrowser.com/features/real-fingerprints): Mosaic draws each profile's GPU, screen, CPU, memory and fonts from one real device, so the parts of a fingerprint agree with each other and with the host.







[A FingerprintJS pass has two halves](https://mosaicbrowser.com/blog/fingerprintjs-visitor-id-two-halves): A FingerprintJS visitor ID should differ per profile and hold across launches of one. Colliding ids link profiles; ids that change flag a spoofer.

## Mosaic, on your own computer

The browser this blog is about: its own Chromium, fingerprints from real machines and a proxy exit per profile. Free holds 3 profiles. Pro is $49 a month for any number.

Mosaic is a desktop app

Email yourself the link and open it on your computer.
