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:
Cross-Origin-Opener-Policy: same-origin(COOP)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.