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.
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.