March 13, 2018 12:37:14by

JavaScript Zero: real JavaScript, and zero side-channel attacks

JavaScript Zero: real JavaScript, and zero side-channel attacks

JavaScript Zero: real JavaScript, and zero side-channel attacks

JavaScript Zero: Real JavaScript and zero side-channel attacks Schwarz et al., NDSS’18

We’re moving from the server-side back to the client-side today, with a very topical paper looking at defences against micro-architectural and side-channel attacks in browsers. Since submission of the paper to NDSS’18, this subject grew in prominence of course with the announcement of the meltdown and spectre attacks.

Chrome Zero is a proof of concept implementation that defends against these attacks. It installs as a Chrome extension and protects functions, properties, and objects that can be exploited to construct attacks. The basic idea is very simple, functions are wrapped with replacement versions that allow injection of a policy. This idea of wrapping functions (and properties with accessor properties, and certain objects with proxy objects) goes by the fancy name of virtual machine layering.

Closures are used when wrapping functions to ensure that references to the original function are inaccessible to any code outside of the closure.

Policies determine what the wrappers actually do. There are four possible alternatives:


With that basic description of the mechanism out of the way, let’s get down to the interesting part: the features in JavaScript that are used in microarchitectural and side-channel attacks, and how Chrome Zero protects them.

The features used to build attacks

The following table provides a nice summary of attacks and the features that they require:


Memory addresses

JavaScript never discloses virtual addresses, but ArrayBuffers can be exploited to reconstruct them. Once an attacker has knowledge of virtual addresses, they have effectively defeated address space layout randomization (ASLR). Microarchitectural attacks typically need physical addresses. Since browser engines allocate ArrayBuffers page aligned, the first byte is therefore at the beginning of a new physical page. Iterating over a large array also results in page faults at the beginning of a new page. The increased time to resolve a page fault is higher than a regular memory access and can be detected.

In Chrome Zero, the challenge is to ensure that array buffers are not page-aligned, and that attackers cannot discover the offset of array buffers within the page. Chrome Zero uses four defences here:

Timing information

JavaScript provides the Date object with resolution of 1 ms, and the Performance object which provides timestamps accurate to a few microseconds. Microarchitectural attacks often require resolution on the order of nanoseconds though. Custom timing primitives, often based on some form of monotonically increment counter, are used as clock replacements.

Chrome Zero implements two timing defences: low-resolution timestamps and fuzzy time.

The following figure shows theses policies at work against an attacker trying to distinguish between fast and slow versions of a function.

This is more than enough to defeat the JavaScript keystroke detection attack of Lipp et al.:


The support for parallelism afforded by web workers provides new side-channel attack possibilities, by measuring the dispatch time of the event queue. For example, an endless loop running within a web worker can detect CPU interrupts, which can then be used to deduce keystroke information.

A drastic but effective Chrome Zero policy is to prevent real parallelism by replacing web workers with a polyfill intended for unsupported browsers that simulates web workers on the main thread. An less drastic policy is to delay the postMessage function with random delays similar to fuzzy timing. This is sufficient to defeat keystroke detection attacks:

Shared data

JavaScript’s SharedArrayBuffer behaves like a normal ArrayBuffer, but can be simultaneously accessed by multiple workers. This shared data can be exploited to build timing primitives with a nanosecond resolution.

One simple Chrome Zero policy is to disallow use of SharedArrayBuffers (which is deactivated by default in modern browsers anyway at the moment). An alternative policy is to add random delays to accesses of shared buffers. This is enough to prevent the high-resolution timing needed by attacks:

Sensor API

Some sensors are already covered by browser’s existing permission systems, but several sensors are not:

The battery status API can also be used to enable a tracking identifier. In Chrome Zero the battery interface can be set to return randomized or fixed values, or disable entirely. Likewise Chrome Zero can return either a fixed value or disable the ambient light sensor API. For motion and orientation sensors data can be spoofed, or access prohibited entirely.


We’ve already seen a number of examples of Chrome Zero preventing attacks. The following table summarises the policies and their effect on attacks:


Back-testing on all 12 CVEs discovered since 2016 for Chrome 49 or later reveals that half (6) of them are prevented. Creating policies to specifically target CVEs was not a goal of the current research.

The performance overhead of Chrome Zero was evaluated on the Alexa Top 10 websites, and correct functioning of sites was evaluated for the Alexa Top 25. Page load times (we’re not told what metric is actually measured) increase from 10.64ms on average, to 89.08ms when all policies are enabled — the overhead is proportional to the number of policies in force. On the JetStream browser benchmark, Chrome Zero shows a performance overhead of 1.54%.

Depending on what an application actually does with arrays, web workers etc., I would expect the impact to be significantly greater in some circumstances. However, in a double-blind user study with 24 participants, the participants were unable to tell whether they were using a browser with Chrome Zero enable or not – apart from on the site.