Reducing performance.now Precision Against Timing Attacks

High-resolution web timers like performance.now() previously exposed sub-microsecond accuracy, creating vulnerabilities that allowed malicious JavaScript to execute hardware-level side-channel and cache-timing attacks. To defend users against threats like Spectre and Meltdown, major browser vendors systematically clamped timestamp resolution, introduced randomized jitter, and gated microsecond precision behind strict isolation policies.

The Threat of High-Resolution Timing

JavaScript execution within a web browser runs in a sandboxed environment. However, hardware-level CPU mechanisms—such as speculative execution, branch prediction, and memory caching—leak information through timing discrepancies. An attacker can measure subtle differences in execution time to determine whether a specific memory address is cached in the CPU L1/L2 cache or fetched from main memory.

Originally, the High Resolution Time specification allowed performance.now() to deliver timestamps with floating-point values accurate to fractions of a microsecond. This level of granularity allowed attackers to infer sensitive data across origins, read unauthorized system memory, and bypass traditional sandbox boundaries.

Timestamp Clamping and Coarsening

The primary defense implemented by browser vendors (such as Google, Mozilla, and Apple) was reducing the resolution of performance.now() by clamping timestamps to coarser intervals.

Instead of returning nanosecond-scale precision, browsers rounded timestamps to broader increments: * Initial mitigations: In the wake of Spectre disclosures, browsers coarsened performance.now() resolution to intervals such as 20 microseconds (µs), 50 µs, or 100 µs. * Modern baselines: Most modern browsers cap the default timer resolution to between 5 µs and 100 µs depending on the platform, device type, and security context.

By eliminating sub-microsecond precision by default, the temporal difference between a CPU cache hit and a cache miss becomes indistinguishable within single-event measurements.

Adding Jitter and Noise

Attackers initially attempted to bypass timestamp clamping through statistical averaging. By repeating a malicious operation thousands of times and computing average elapsed times, an attacker could mathematically reconstruct fine-grained timing information despite coarsened timestamps.

To counter statistical reconstruction, vendors introduced artificial jitter (randomized noise) into the timer results. When jitter is applied, timestamps are slightly perturbed or randomly rounded either up or down. This injects non-deterministic variance into the measurements, significantly degrading an attacker’s ability to infer hardware states through statistical aggregation.

Cross-Origin Isolation and Safe Precision Restoration

While reduced precision mitigated side-channel attacks, legitimate web applications (such as audio processors, web games, and performance profilers) still required accurate timing.

To safely restore higher precision, browsers implemented cross-origin isolation. High-resolution timers are only restored to sub-microsecond precision (typically up to 5 µs without artificial jitter) if a website enforces the following HTTP response headers:

  1. Cross-Origin-Opener-Policy: same-origin (COOP)
  2. Cross-Origin-Embedder-Policy: require-corp (COEP)

These headers ensure that the webpage runs in a dedicated operating system process separate from other websites, preventing cross-origin data from residing in the same memory address space and neutralizing the threat model that timing exploits rely on.