Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Blocking fingerprinting in browser settings can cause icons not to render or map not to render #8377

Closed
kbauhausness opened this issue Jun 21, 2019 · 12 comments
Assignees

Comments

@kbauhausness
Copy link
Contributor

kbauhausness commented Jun 21, 2019

The browser Brave's "device recognition"/"fingerprinting" blocking blocks anything that attempts to read data out of a canvas to prevent it from being used for browser fingerprinting (see https://browserleaks.com/canvas). This defaults to "block 3rd-party fingerprinting", which can apply when a map is embedded in an external website.

More on Google/Chrome's future plans for anti-fingerprinting functionality here.

mapbox-gl-js version: 1.0.0, any

browser: able to reproduce in Brave, Chrome given anti-fingerprinting settings enabled

Steps to Trigger Behavior

In Brave:

  1. change the setting (under Settings > Shields) to "Block all fingerprinting",
  2. you can reproduce with this simple page:
<!DOCTYPE html>
<html>
    <body>
    <canvas id="canvas" width="100" height="100"></canvas>
    <div id="output"></div>
    <script type="text/javascript">
        var canvas = document.getElementById("canvas");
        var ctx = canvas.getContext("2d");
        ctx.fillStyle = "#FF0000";
        ctx.fillRect(0, 0, 100, 100);
        var imageData = ctx.getImageData(0, 0, 100, 100);
        document.getElementById("output").innerHTML = "getImageData width: " + imageData.width + " height: " + imageData.height;
    </script>
</html>
  1. You'll see that in a normal browser, the expected width and height of 100x100 are seen, but with that setting in Brave, it returns a 0x0 image.
  2. Similarly, setting Brave to "block all fingerprinting" and loading this mapbox map, the error RangeError('out of range destination coordinates for image copy') is thrown and no map is rendered: https://api.mapbox.com/styles/v1/mapbox/streets-v9.html?title=true&access_token=ACCESS_TOKEN#1.1/0/0 .

Screen Shot 2019-06-21 at 11 59 31 AM

In Chrome:

  1. install the Chrome extension CanvasFingerprintBlock
  2. load the map https://api.mapbox.com/styles/v1/mapbox/streets-v9.html?title=true&access_token=ACCESS_TOKEN#1.1/0/0
  3. Notice that the map loads, but icons are missing and the following message is shown in the CanvasFingerprintBlock extension:

Screen Shot 2019-06-21 at 12 02 30 PM

Screen Shot 2019-06-21 at 12 01 53 PM

Link to Demonstration

https://jsbin.com/

Expected Behavior

The map is still rendered / icons are still rendered even when anti-fingerprinting settings are enabled.

Actual Behavior

In Brave with anti-fingerprinting, the map is not rendered. In Chrome with anti-fingerprinting, the icons from the spritesheet are not rendered.

@peterqliu peterqliu added the needs investigation 🔍 Issues that require further research (e.g. it's not clear whether it's GL JS or something else) label Jun 24, 2019
@peterqliu
Copy link
Contributor

peterqliu commented Jun 24, 2019

Flagging the current getImageData implementation:

getImageData(img: CanvasImageSource): ImageData {
const canvas = window.document.createElement('canvas');
const context = canvas.getContext('2d');
if (!context) {
throw new Error('failed to create canvas 2d context');
}
canvas.width = img.width;
canvas.height = img.height;
context.drawImage(img, 0, 0, img.width, img.height);
return context.getImageData(0, 0, img.width, img.height);
},

May be worth exploring get-pixels's canvas-less, node implementation https://github.com/scijs/get-pixels/blob/master/node-pixels.js

@1ec5
Copy link
Contributor

1ec5 commented Jun 24, 2019

Is TinySDF also affected? TinySDF is used for rendering ideographic characters using client-side fonts.

https://github.com/mapbox/tiny-sdf/blob/34a06b10e99555a156e4d147ab28fd11da4bd4e0/index.js#L40

@ahk
Copy link
Contributor

ahk commented Jun 24, 2019

Is TinySDF also affected?

It looks like it is. It's using the same function of canvas API.

@ryanhamley
Copy link
Contributor

It definitely blocks local ideograph generation as well. I just tested it out to be sure.

Screen Shot 2019-06-24 at 4 37 37 PM

@ryanhamley
Copy link
Contributor

ryanhamley commented Jun 27, 2019

It looks like the ImageData interface may be what we need to avoid this.

Creates an ImageData object from a given Uint8ClampedArray and the size of the image it contains. If no array is given, it creates an image of a black rectangle. Note that this is the most common way to create such an object in workers as createImageData() is not available there.

EDIT: Ooops, misread the description. This won't work.

cc @asheemmamoowala @mourner Is this what you had in mind?

@mourner
Copy link
Member

mourner commented Jun 27, 2019

@ryanhamley no, that's the whole point of TinySDF — we need to use getImageData to get the data drawn on the canvas with the Canvas text API. It won't work without it. createImageData is only useful for the reverse — generating an array of pixel data that you want drawn on a Canvas.

@asheemmamoowala
Copy link
Contributor

According to the docs, createImageBitmap can be used to get the image data out of a <canvas> or HTMLCanvasElement as well. Shouldn't that be able to replace a canvasElem.getImageData() call?

@ryanhamley
Copy link
Contributor

ryanhamley commented Jun 28, 2019

I believe after a bit of research that canvas fingerprinting is what's blocking sprites and glyphs. But there's also WebGL fingerprinting and blocking this causes the entire map to render as a white screen. That's why we see a difference between Chrome using the CanvasFingerprintBlock plugin and Brave. When you disable all fingerprinting in Brave, it blocks both Canvas and WebGL fingerprinting.

Under Technical Details, Brave says:

Because most browser fingerprinting defense requires disabling web features that are required for many sites to work properly, it is implemented as off-by-default for now (can be turned on in about:preferences globally, or on a per-site basis in the Bravery panel). We will consider turning it on-by-default when we have fingerprinting detection heuristics with a sufficiently-low false positive rate.

You can create an ImageBitmap from a canvas element, but the ImageBitmap instance only has height and width properties; it does not expose the image data the way that canvas.getImageData does (which returns an instance of ImageData).

@mourner
Copy link
Member

mourner commented Jul 1, 2019

@ryanhamley does this mean that there are no next actions for us here — we just wait for browsers to get better at not blocking essential features when turning on fingerprinting protection, and rely on users not turning it fully on until that time — right?

@ryanhamley
Copy link
Contributor

That's what my research is pointing towards @mourner I want to do a bit more testing today before I close this though.

@ryanhamley
Copy link
Contributor

I did some fairly extensive research on this issue and there's not really anything we can do about it. Fingerprinting blockers disable functionality that GL JS relies on and there are no alternative ways of making the library work. This is a won't fix issue.

@andrewharvey
Copy link
Collaborator

In light of the unable to fix here I've opened mapbox/mapbox-gl-supported#25 in case mapbox-gl-supported can detect the potential issue and return false.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

8 participants