Clocks

Clocks

October 1, 2020

We can distinguish two types of clocks – explicit and implicit. Explicit clocks are used by developers to get direct timing measurements, mechanisms of this type are offered explicitly by the browser. In contrast, implicit clocks utilize particular web features to create unintended clocks that allow measuring the relative passage of time.

Explicit Clocks #

performance.now API #

The performance.now() API allows developers to get high-resolution timing measurements.

info

In order to mitigate some types of XS-Leaks, performance.now()’s accuracy was reduced from a range of nanoseconds to microsecond precision in all modern browsers 1 2 3.


  1. Reduce resolution of performance.now (Webkit). link ↩︎

  2. Reduce precision of performance.now() to 20us (Gecko). link ↩︎

  3. Reduce resolution of performance.now to prevent timing attacks (Blink). link ↩︎

Date API #

The Date API is the oldest API present in browsers which can be used to obtain timing measurements. It allows developers to get dates, and get Unix timestamps with Date.now(). These measurements are much less precise than performance.now(). Before the introduction of newer APIs, attacks used to leverage this API 1.

Implicit Clocks #

SharedArrayBuffer and Web Workers #

With the introduction of Web Workers, new mechanisms to exchange data between threads were created 2. One of those mechanisms is SharedArrayBuffer which provides memory sharing between the main thread and a worker thread. A malicious website can create an implicit clock by loading a worker running an infinite loop that increments a number in the buffer. This value can then be accessed by the main thread at any time to read how many incrementations were performed.

info

SharedArrayBuffer was removed from browsers with the publication of Spectre. It was reintroduced later in 2020, requiring documents to be in a secure context to make use of the API. Since secure contexts cannot reference any cross-origin content that has not explicitly opted in to being accessed, this means SharedArrayBuffers cannot be used as clocks for some XS-Leaks.

To make use of SharedArrayBuffer in modern browsers, an application needs to explicitly opt in to COOP and COEP by setting the following headers:

Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Embedder-Policy: require-corp
// Define a function to be ran inside a WebWorker
function worker_function() {
  self.onmessage = function (event) {
    const sharedBuffer = event.data;
    const sharedArray = new Uint32Array(sharedBuffer);

    // Infinitely increase the uint32 number
    while (true) Atomics.add(sharedArray, 0, 1);
  };
}

// Create the WebWorker from the JS function and invoke it
const worker = new Worker(
  URL.createObjectURL(
    new Blob([`(${worker_function})()`], {
      type: "text/javascript"
    }))
);

// Create a Shared buffer between the WebWorker and a document
const sharedBuffer = new SharedArrayBuffer(Uint32Array.BYTES_PER_ELEMENT);
const sharedArray = new Uint32Array(sharedBuffer);
worker.postMessage(sharedBuffer);

tip

To get the relative time in a main thread, you can use the Atomics API.

Atomics.load(sharedArray, 0);

Other Clocks #

There are a considerable number of APIs attackers can abuse to create implicit clocks: Broadcast Channel API, Message Channel API, requestAnimationFrame, setTimeout, CSS animations, and others 3 4.

References #


  1. Exposing Private Information by Timing Web Applications, link ↩︎

  2. Shared memory: Side-channel information leaks, link ↩︎

  3. Fantastic Timers and Where to Find Them: High-Resolution Microarchitectural Attacks in JavaScript, link ↩︎

  4. Trusted Browsers for Uncertain Times, link ↩︎