On Mon, Sep 1, 2014 at 1:15 PM, Mike Perry mikeperry@torproject.org wrote:
Arthur D. Edelstein:
Hi All,
I'm wondering about the history of JS fingerprinting mitigation in Tor Browser. What prompted the change of approach from JavaScript hooks to C++ patches? I had read something about a race condition discovered, but I haven't found more details.
I've been thinking about the idea of developing a C++ patch for Tor Browser (and Firefox) that allows extensions to securely replace arbitrary members (functions and properties) of the global window object at runtime, before content is loaded. By "secure" I mean that, by design, there would be no workaround for content scripts to access the original window object members. (Maybe this capability already exists -- I don't know.)
The advantages of this monkey patching approach over addressing fingerprinting vulnerabilities with C++ patches is (1) it would (I think) simplify fingerprinting countermeasures, and (2) it would reduce the number of Firefox C++ patches that Mozilla needs to accept.
Is this idea worth pursuing further?
Possibly, but even if we address all the race conditions and edge cases (like javascript: urls and data: urls that contain script), historically JS hooking is very fragile, with lots of ways to undo/break it cropping up regularly in new releases.
IIRC, such "secure"/"sealed"/"non-configurable"/non-revocable hooking recently became standardized as part of Object.defineProperty, Object.seal, and friends, but this may still have issues. Some things might still be forbidden from being overridden for various reasons. The last time I went down this road, it was an endless sinkhole of issues, and I only did it because patching the browser was not an option at that point.
On top of this, it doesn't allow us to cleanly hook events, callback parameters, and other async things. In some cases, we can probably work around this by hooking the callback registration to install our own wrapper callbacks that alter their args before passing them in to the registered callback, but that too takes care to ensure that the true arguments cannot be discovered by inspecting the callchain and associated parent scopes from inside the final content's callback.
It also won't help with CSS-based vectors for fingerprinting, or complicated interactions between styling and JS.
It's something to consider if it really looks like Mozilla won't take any of our fingerprinting defenses, but I think we should continue to prioritize direct patches for now.
That said, if you see a clean way to create an API to do secure script injection and feel like hacking it up real quick, feel free. It may prove useful eventually, but I suspect we'll uncover a whole slough of surprises once we actually try to use it. We'll probably also need regression tests in-tree for every single function/callback/property we hook, to make sure that an implementation change doesn't suddenly break our ability to hook something in the way we want.
You make a compelling argument for the direct C++ patches. My thinking mainly was to use JS patches as a stopgap while C++ patches are being developed, but after your explanation it's not so clear to me that JS patches will be any easier or quicker.