I suspected that the test for DPI at browserspy.dk was not functioning properly, so I kind of dared people on Twitter to come up with a PoC for using relative element sizing to infer true DPI, beating Tor Browser's DPI-spoofing. 0xPoly reported that the true DPI size can be inferred via such a mechanism, and provided the following example PoC:
In Tor Browser, even on high-density displays, the DPI is correctly spoofed to 96x96, and the above code does alert('96'). However, if the user changes the zoom level, i.e. via Ctrl-+ or Ctrl--, then the above Javascript will detect a non-96x96 DPI. When I tested (on a machine with a 96x96 DPI display), zooming once led to alert('115.20000457763672'), however that '115.20000457763672' stayed the same if I scaled the browser window size differently and reloaded the page (keeping the zoom at the same level). Peter Todd reported that detecting the zoom level also works on a high-density display.
This may particularly be a problem on huge displays, or any other displays probably viewed from a greater-than-arms-length distance, where the users are constantly zooming in.
I'm less concerned about zoom being detectable, especially since the process of zooming will cause all sorts of CSS reflows that will likely be detectable no matter what, at least with how zoom is currently implemented.
There's also #4316 (moved) though, which is perhaps more concerning. I wonder if #4316 (moved) is still even an issue though. That sure is an old ticket.
I didn't understand that either… perhaps that person will comment more. Thanks, mikeperry, for cataloging that tweet for me; I nearly forgot to add it to the ticket.
hello, poly here. Sorry for the late reply, I'm just getting used to the trac system.
I created a writeup (with photos!) to clarify the windows dpi scaling and another POC to show that the actual zoom level can be inferred. Skip to the description section for the relevant stuff. Can anyone try the second POC and comment on how reliable it is?
hello, poly here. Sorry for the late reply, I'm just getting used to the trac system.
I created a writeup (with photos!) to clarify the windows dpi scaling and another POC to show that the actual zoom level can be inferred. Skip to the description section for the relevant stuff. Can anyone try the second POC and comment on how reliable it is?
I did often get no zoom level back while zooming around but maybe that's just because the zoom level was not exactly hard-coded. But I could confirm that #4316 (moved) is fixed, thanks. That said this ticket is a duplicate of #8076 (moved).
Trac: Resolution: N/Ato duplicate Status: new to closed
Reopened this to dup the other way, ie: calling #8076 (moved) the dup (or simply closed, as this ticket contains the results of investigation).
Well, the investigation is not over, there is still the CSS case and the things we wanted to study (large amount of variability) which is why I duped it the way I did. That said I don't feel so strongly that I'd start duping this over again but I think we should make sure that a fix we provide for this particular PoC catches all the other DPI-spoofing-is-broken-scenarios, too.
Oh, and while I am at it: the issue is visible with a default Tor Browser without messing with the zoom at all (which is why I changed the summary and raised the priority).
Trac: Priority: normal to major Keywords: N/Adeleted, tbb-testcase, tbb-firefox-patch added Summary: Tor Browser DPI spoofing is broken if the user changes zoom level to Tor Browser DPI spoofing is broken
Also looks good to me. poly: feel free to reopen this or open a new ticket if you notice any remaining issues. This fix will appear in 4.5-stable.
Trac: Status: needs_review to closed Summary: Tor Browser DPI spoofing is broken to Tor Browser DPI spoofing omitted window.devicePixelRatio Resolution: N/Ato fixed
It turns out my fix made it impossible for chrome code to read the true devicePixelRatio of a content window, which is needed for my patch in #14429 (moved) (and may be useful in general). So here's another fixup which continues to spoof devicePixelRatio for content code, but returns the true value for chrome code:
My typical question with this IsCaller stuff: Is this property exported to WebSockets? What happens there?
Are you thinking of WebWorkers? I ran a quick manual test, and devicePixelRatio is not exposed to WebWorkers.
Also, how about scripts inside blob URIs from the URL bar? And blob URIs from an iframe?
Yeah, again I think you're right that the IsCallerChrome() call is dangerous, and I should have thought about these possibilities more. Also it worries me that using IsCallerChrome to prevent leaks to content is not very future-proof, even if we can confirm that it is airtight now.
An alternative method for getting the "true zoom level" of a content window, instead of
let trueZoom = gBrowser.contentWindow.devicePixelRatio;
is to call
let trueZoom = gBrowser.contentWindow.QueryInterface(Ci.nsIInterfaceRequestor) .getInterface(Ci.nsIDOMWindowUtils) .screenPixelsPerCSSPixel;
So here's an alternative patch that leaves nsGlobalWindow::GetDevicePixelRatio with the IsChrome call and instead fixes nsDOMWindowUtils::GetScreenPixelsPerCSSPixel so that it isn't spoofed when "privacy.resistFingerprinting" is activated. The latter call is only available from chrome code.