Opened 6 years ago

Closed 6 years ago

#13021 closed task (fixed)

Prompt before allowing Canvas isPointIn*() calls

Reported by: mikeperry Owned by: brade
Priority: High Milestone:
Component: Applications/Tor Browser Version:
Severity: Keywords: ff31-esr, tbb-fingerprinting, TorBrowserTeam201409, MikePerry201409R
Cc: mcs Actual Points:
Parent ID: Points:
Reviewer: Sponsor:

Description

There were some changes to the HTML5 canvas object. I think toBlob() was added, and possibly other extractors as well:
https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D

There is now also a hint that JS wants to read the canvas. Probably irrelevant to us, but worth looking at in more detail:
https://bugzilla.mozilla.org/show_bug.cgi?id=884226

There's also something called HitRegions. I'm not sure what these are at all:
https://bugzilla.mozilla.org/show_bug.cgi?id=966591

Child Tickets

Attachments (3)

path.html (2.2 KB) - added by dcf 6 years ago.
measureTextFP.html (2.5 KB) - added by mcs 6 years ago.
canvas measureText() Test
0001-Bug-13021-Prompt-before-allowing-Canvas-isPointIn-ca.patch (5.6 KB) - added by mcs 6 years ago.
proposed patch

Download all attachments as: .zip

Change History (15)

comment:1 Changed 6 years ago by mcs

Cc: mcs added
Owner: changed from tbb-team to brade
Status: newassigned

comment:2 Changed 6 years ago by gacar

I checked https://bugzilla.mozilla.org/show_bug.cgi?id=884226: This brings a new canvas context property (willReadFrequently) that enables reading from a software backend instead of a hardware "accelerated" one, which turns out to be super-slow for some cases.

So, canvas reads may get faster, but no fingerprinting risks that I can see.

Also interesting is #962517 which brings a chrome only HW-acceleration-disabled canvas and explains the HW backend problem better in the context of FFOS.

HitRegions are about defining clickable regions in canvas, similar to image-maps for <img> elements (e.g. using <area> & <map>). Although one may potentially exploit the pixel-level differences in region boundaries (similar to isPointInPath method AddThis was using) it requires user interaction (click or hover) and doesn't look like a reliable fingerprinting vector.

Also there's a switch canvas.hitregions.enabled, and it is disabled by default in ESR31.

comment:3 in reply to:  2 ; Changed 6 years ago by dcf

Replying to gacar:

I checked https://bugzilla.mozilla.org/show_bug.cgi?id=884226: This brings a new canvas context property (willReadFrequently) that enables reading from a software backend instead of a hardware "accelerated" one, which turns out to be super-slow for some cases.

If a browser has both accelerated and non-accelerated canvases, they probably don't render things identically. You could draw the same fingerprinting issue in both and hash them together, and thereby get more discriminating power than one canvas alone.

I agree that willReadFrequently doesn't add any new fingerprinting power, because in its absence you just have to game whatever heuristic decides whether you get an accelerated or non-accelerated canvas.

comment:4 in reply to:  3 Changed 6 years ago by gacar

Replying to dcf:

Replying to gacar:

I checked https://bugzilla.mozilla.org/show_bug.cgi?id=884226: This brings a new canvas context property (willReadFrequently) that enables reading from a software backend instead of a hardware "accelerated" one, which turns out to be super-slow for some cases.

If a browser has both accelerated and non-accelerated canvases, they probably don't render things identically. You could draw the same fingerprinting issue in both and hash them together, and thereby get more discriminating power than one canvas alone.

I agree that willReadFrequently doesn't add any new fingerprinting power, because in its absence you just have to game whatever heuristic decides whether you get an accelerated or non-accelerated canvas.

Yep, makes it easier to get a 2-in-1 canvas fingerprint from standard browsers.

But it's not a threat for Tor Browser, I suppose. Since TB returns a white canvas regardless of HW/SW backend.

comment:5 Changed 6 years ago by mcs

Kathy and I also reviewed the canvas APIs. Here are a few of our observations:

  • The willReadFrequently canvas option is disabled by default (the gfx.canvas.willReadFrequently.enable pref must be added with the value true) so we do not need to worry about this.
  • We have not done anything to block use of isPointInPath() and isPointInStroke(). Do we need to block these?
  • We have not done anything to block use of measureText(). Theoretically, it could be used to detect differences based on available fonts or rendering differences. Do we need to block this?
  • In ESR31, ToBlob() accepts options to allow callers to specify encoding options such as JPEG quality. Kathy and I do not think this is a fingerprinting vector since, by default, white image data is returned.

comment:6 in reply to:  5 ; Changed 6 years ago by gacar

Replying to mcs:

Kathy and I also reviewed the canvas APIs. Here are a few of our observations:

  • We have not done anything to block use of isPointInPath() and isPointInStroke(). Do we need to block these?

I could not find any way to exploit those two for fingerprinting, but better someone else give a shot too.

Some canvas fingerprinting scripts found to use isPointInPath() with "even-odd" winding rule, but I think this was just to check browser support - will be same for all TBs. Unless someone says "the internal representations of the paths may depend on the graphics stack too!"

One could use these two functions to probe system fonts, if adding text to the current path or stroke was possible. I tried strokeText() and fillText() followed by isPointInStroke() and isPointInPath() but it didn't work out.

  • We have not done anything to block use of measureText(). Theoretically, it could be used to detect differences based on available fonts or rendering differences. Do we need to block this?

Wow, that's a good catch! I think this should certainly be blocked.

Changed 6 years ago by dcf

Attachment: path.html added

comment:7 in reply to:  6 Changed 6 years ago by dcf

Replying to gacar:

Replying to mcs:

Kathy and I also reviewed the canvas APIs. Here are a few of our observations:

  • We have not done anything to block use of isPointInPath() and isPointInStroke(). Do we need to block these?

I could not find any way to exploit those two for fingerprinting, but better someone else give a shot too.

Some canvas fingerprinting scripts found to use isPointInPath() with "even-odd" winding rule, but I think this was just to check browser support - will be same for all TBs. Unless someone says "the internal representations of the paths may depend on the graphics stack too!"

It might be possible to get some mileage out of floating-point precision issues. For example, attachment:path.html draws quarter-circles in different ways (with arc and bezierCurveTo and quadraticCurveTo) and with different transformation matrices, and then tests points right on the perimeter of the circle.

I see different fingerprints in different versions of Firefox on the same system, and the same version of Tor Browser on different operating systems. I didn't get different fingerprints for the same Tor Browser on the same OS, but I only tried three installations.

The fingerprint I get with Tor Browser 4.0-alpha-2 on Debian is:

arc
( T F F F T T ) ( T F F F T T ) ( T F F F T T )
arc prerotated
( F F F T T T ) ( F F F T T T ) ( F F F T T T )
bezierCurve
( T F F F T T ) ( T F F F T T ) ( T F F F T T )
bezierCurveTo prerotated
( F F F T T T ) ( F F F T T T ) ( F F F T T T )
quadraticCurve
( T T T T T T ) ( T T T T T T ) ( T T T T T T )
quadraticCurve prerotated
( F F F T T T ) ( F F F T T T ) ( F F F T T T )

We can guess that the underlying representation for arc is a cubic Bezier, because the corresponding rows match. The one for Firefox 31 on the same Debian is a little different:

arc
( T F F F T T ) ( T F F F T T ) ( T F F F T T )
arc prerotated
( F F F T T T ) ( F F F T T T ) ( F F F F T T )
bezierCurve
( T F F F T T ) ( T F F F T T ) ( T F F F T T )
bezierCurveTo prerotated
( F F F T T T ) ( F F F T T T ) ( F F F F T T )
quadraticCurve
( T T T T T T ) ( T T T T T T ) ( T F T F T T )
quadraticCurve prerotated
( F F F T T T ) ( F F F T T T ) ( F F F F T T )

Tor Browser 4.0-alpha-1 on Windows 8 is:

arc
( T T T T T T ) ( F T T T T T ) ( F T F F F T )
arc prerotated
( F F F T T T ) ( F F F T T T ) ( F F T T F T )
bezierCurve
( T T T T T T ) ( F T T T T T ) ( F T F F F T )
bezierCurveTo prerotated
( F F F T T T ) ( F F F T T T ) ( F F T T F T )
quadraticCurve
( T T T T T T ) ( F T T T T T ) ( F T F F F T )
quadraticCurve prerotated
( F F F T T T ) ( F F F T T T ) ( F F T T F T )

The fact that the result of isPointInPath can change just by applying a different transformation matrix (even though the test point gets multiplied by the same matrix) shows that there are probably some measurable precision issues. Whether they could ever distinguish the same Firefox on the same OS, I don't know.

comment:8 Changed 6 years ago by gacar

Cool, seems all Canvas API methods are in a competition to be the most fingerprintable!

I got matching results with you, i.e. my FF 32 matched your FF 31, my Tor Browser matched yours.

I think this may get really scary if we observe different fingerprints for the same TBB version on different machines (with the same OSes).

Until seeing that, I will believe that Tor Browser is ok with this attack. Since the diversity should be due to the drawing target (cairo SW, common for TBB users) which does the transformation and boundary calculation:
https://mxr.mozilla.org/mozilla-esr31/source/content/canvas/src/CanvasRenderingContext2D.cpp#898
https://mxr.mozilla.org/mozilla-esr31/source/content/canvas/src/CanvasRenderingContext2D.cpp#3110

Being said that, I think that really worth testing!

Changed 6 years ago by mcs

Attachment: measureTextFP.html added

canvas measureText() Test

comment:9 in reply to:  6 Changed 6 years ago by mcs

Replying to gacar:

Wow, that's a good catch! I think this should certainly be blocked.

Using the measureTextFP.html page that I just attached, the results we generated are interesting. It seems like the approach used to fix #2872 should apply to canvas measureText() as well. And adjusting the browser.display.max_font_attempts and browser.display.max_font_count prefs does significantly reduce the number of unique measureText() values a web page can generate.

Here are the results that we got:

Browser Default / max_font prefs set to 10 max_font prefs set to -1
Mac OS – Firefox 24.0 17 unique widths n/a
Mac OS – Tor Browser 3.6.5 10 unique widths 17 unique widths
Win7 – Firefox 24.0 11 unique widths n/a
Win7 – Tor Browser 3.6.5 6 unique widths 11 unique widths
Ubuntu 12.04 – Firefox 24.0 5 unique widths n/a
Ubuntu 12.04 – Tor Browser 3.6.5 4 unique widths 5 unique widths

I guess the fonts we used in measureTextFP.html are somewhat "optimized" for Mac OS (we copied most of them from http://www.lalit.org/lab/javascript-css-font-detect/).

Last edited 6 years ago by mcs (previous) (diff)

comment:10 Changed 6 years ago by gacar

Brilliant! I think this also makes fixing the multi-frame issue in #5798 more important.

Since it is the font-limit that bounces this attack, and one can embed multiple canvases in different iframes.

BTW I just remember that I once tested if font-limits apply to canvas as well:
http://jsbin.com/ferit/
As your findings also confirm the limits apply to canvas, which is good.

Back then apparently I was aware that measureText can be used for fingerprinting, but I guess I thought it gives more or less the same info as CSS offsetWidth/Height measurement. Having seen dcf's demo with transformations (comment 7), now I'm not very sure about that...

comment:11 Changed 6 years ago by mcs

Keywords: MikePerry201409R added
Status: assignedneeds_review
Summary: Review Canvas APIs for fingerprintabilityPrompt before allowing Canvas isPointIn*() calls

Re-casting this bug as "Prompt before allowing Canvas isPointIn*() calls". I will attach a patch in a moment. Mike and others: please review.

comment:12 Changed 6 years ago by mikeperry

Resolution: fixed
Status: needs_reviewclosed

Ok, I applied this to our new tor-browser-31.1.1esr-4.x-1 branch. Thanks!

Note: See TracTickets for help on using tickets.