Execution Timing

Execution Timing

October 1, 2020
Abuse Event Loop, Service Workers, Site Isolation, CSS Injections, Regex Injections, iframes
Category Attack
Defenses Fetch Metadata, SameSite Cookies, COOP, Framing Protections

Measuring the time of JavaScript execution in a browser can give attackers information on when certain events are triggered, and how long some operations take.

Timing the Event Loop #

JavaScript’s concurrency model is based on a single-threaded event loop which means it can only run one task at a time. If, for example, some time-consuming task blocks the event loop, the user can perceive a freeze on a page as a result of the UI thread being starved. Other tasks must wait until the blocking one runs to conclusion. Each browser implements different process models, which means some web sites might run in different threads (and event loops) depending on their relations.

Some techniques can exploit this model to steal secrets from a cross-origin page:

  • Infer how long code from a different origin takes to run by measuring how long it takes to run next in the event pool 1 2. The attacker keeps sending events to the event loop with fixed properties, which will eventually be dispatched if the pool is empty. Other origins will dispatch events to the same pool, and this is where an attacker infers the timing difference by detecting if a delay occurred with one of its tasks.
  • Steal a secret from a cross-origin page if the said secret is being compared by an attacker-controlled string. The leak is a result of comparing timing differences in the event loop of a char-by-char string comparison 2 (using the previous technique). In browsers without process isolation, cross-window communications between different origins will run in the same thread, thus sharing the same event loop.


This attack is no longer possible in Browsers with process isolation mechanisms in place. Such mechanisms are only present in Chromium-Based browsers with Site Isolation and soon in Firefox under Project Fission.

Busy Event Loop #

Another technique to measure JavaScript Execution consists of blocking the event loop of a thread and timing how long it takes for the event loop to become available again. One of the main advantages of this technique is its ability to circumvent Site Isolation as an attacker origin can mess with the execution of another origin. The attack works as follows:

  1. Navigate the target website in a separate window with window.open or inside an iframe (if Framing Protections are not in place).
  2. Wait for the long computation to start.
  3. Load any same-site page inside an iframe, regardless of any Framing Protections.

An attacker can detect how long the target website is executed by timing how long it took for the iframe (in step 3) to trigger the onload event (Network Timing of step 3 should be minimal). Since both navigations occurred within the same context and they are same-site, they run in the same thread and share the same event loop (they can block each other).

// Open a new window to measure how long the window blocks the event loop
// for the site example.org

// TODO: Wait for the expensive window to load, e.g. via timeout
// then create an iframe to the same site
var ifr = document.createElement('iframe');
ifr.src = "https://example.org";

// Measure the initial time
var start = performance.now();

ifr.onload = () => {
    // When the iframe loads calculate the time difference
    var time = performance.now() - start;
    console.log('It took %d ms to load the window', time);

Service Workers #

Service Workers can be used to offer offline solutions to web applications but they can be abused by attackers to measure the timing of javascript execution3. They serve as a proxy between the browser and the network and allow applications to intercept any network requests made by the main thread (document).

To make a timing measurement an attacker can perform the following steps:

  1. The attacker registers a service worker in one of its domains (attacker.com).
  2. In the main document, the attacker issues a navigation (window.open) to the target website and instructs the Service Worker to start a timer.
  3. When the new window starts loading the attacker will navigate the reference obtained in step 2 to a page handled by the service worker.
  4. When the request performed in step 3 arrives to the Service Worker it will return a 204 (No Content) response, which will abort the navigation.
  5. At this point the Service Worker will collect a measurement from the timer started in step 2. This measurement will be affected by how long JavaScript blocked the navigation for.

Since no navigation actually occurs, steps from 3 to 5 can be repeated to get more measurements on successive JavaScript execution timings.

CSS Injections #


This group of XS-Leaks requires a CSS Injection on the target page.

Among the different CSS Injection vectors, the most noticeable one is the abuse of CSS Selectors. They can be used as an expression to match and select certain HTML elements. For example, the selector input[value^="a"] will be matched if the value of an input tag starts with the character “a”. So, to detect if a CSS Selector matched the expression, attackers could trigger a callback to one of their websites using certain properties like background, @import, etc 4 5. The matching process can be easily brute-forced, and extended to the full string.

jQuery, CSS Selectors & Short-circuit Timing #

Attackers can abuse another interesting behavior of CSS selectors which is short-circuit evaluation of expressions. This expression is received in an URL hash and evaluated if the page executes jQuery(location.hash) 6.

A timing attack is possible because the expression is compared from right to left, so if the selector main[id='site-main'] does not match and fails to evaluate, the other parts of the selector (*:has(*:has(*:has(*))))) which take longer to execute, are ignored (just like the and operator but backwards).

$("*:has(*:has(*:has(*)) *:has(*:has(*:has(*))) *:has(*:has(*:has(*)))) main[id='site-main']")


In browsers with process isolation mechanisms, Service Workers can be abused to obtain the execution timing measurement or tricks like Busy Event Loop tricks to circumvent Site Isolation.

ReDoS #


This group of XS-Leaks requires an injection of Regex Expressions on the target page.

Regular Expression Denial of Service (ReDoS) is a technique which results in a Denial of Service in applications that allow Regex as user input 2 7. Maliciously crafted regular expressions can be made to run in exponential time. This can be used as an XS-Leak vector if a regex can be injected that has a different runtime depending on some data on the page. This could happen on the client-side or the server-side.

Defense #

Attack Alternative SameSite Cookies (Lax) COOP Framing Protections Isolation Policies
T. Event Loop NIP
Service Workers ✔️ ✔️ NIP
jQuery ✔️ NIP
ReDoS ✔️ NIP
Busy Event Loop ✔️ ✔️ NIP

References #

  1. Loophole: Timing Attacks on Shared Event Loops in Chrome, link ↩︎

  2. Matryoshka - Web Application Timing Attacks (or.. Timing Attacks against JavaScript Applications in Browsers), link ↩︎

  3. Security: XS-Search + XSS Auditor = Not Cool, link ↩︎

  4. CSS Injection Primitives, link ↩︎

  5. HTTPLeaks, link ↩︎

  6. A timing attack with CSS selectors and Javascript, link ↩︎

  7. A Rough Idea of Blind Regular Expression Injection Attack, link ↩︎