Chrome Exposes the Real OS Behind Every Web Request: The Fingerprinting Issue via Math.tanh
Chrome Exposes the Real OS Behind Every Web Request: The Fingerprinting Issue via Math.tanh
A single calculation in the console is enough to unveil the operating system behind a Chromium browser. Researchers from Scrapfly have demonstrated that Math.tanh, along with CSS trigonometric functions and some Web Audio modules, returns slightly different results depending on whether the engine runs on Windows, macOS, or Linux.
The problem arises from an apparently innocuous technical detail. The IEEE 754 standard defines how a double number should be stored but does not require functions like sine, cosine, or hyperbolic tangent to be rounded identically on each platform. Each operating system relies on its own math library, known as libm: glibc on Linux, libsystem_m on macOS, and UCRT (ucrtbase.dll) on Windows.
The result is that Math.tanh(0.8) returns 0.6640367702678491 on Linux, 0.664036770267849 on macOS, and 0.6640367702678489 on Windows: three values that differ by only one unit in the last binary digit (1 ULP), but are sufficient to build a lookup table between bits and operating systems. A site wishing to verify the authenticity of a declared macOS User-Agent can compare that single output and catch out a browser that is actually running on Linux underneath.
Why Mathematical Fingerprinting is Harder to Forge Than Expected
The loophole is recent. Until Chrome 147, the V8 engine calculated tanh using its own internal implementation (a port of fdlibm), identical on every platform. The commit that introduced the direct call to std::tanh, incorporated from V8 version 14.8.57, shifted the calculation towards the host operating system's library: from Chrome 148 onwards, the function thus reads the specific bits of the underlying platform.
Masking the anomaly is not simple. Adding random noise to the results does not work because a reference system would immediately recognize a value that does not correspond to any real operating system, and variability itself would become a hint.
The team had to reconstruct the minimax coefficients and reduction constants of the original libraries bit by bit, also managing non-trivial complications: on Apple Silicon, two math libraries that disagree coexist (the scalar libsystem_m and the vectorial Accelerate), while ARM and x86 architectures handle fused multiply-add instructions differently.
For Windows, the most reliable approach turned out to be directly mapping the original UCRT library into memory and calling its functions, adhering to the ms_abi calling convention typical of the Windows x64 ABI. Final validation required a harness of 871,000 inputs for each release, compared with a real Mac that computes both scalar and vectorial results.
This matter demonstrates how deeply anti-bot systems can push, far beyond canvas, WebGL, or fonts. A trigonometric calculation, deterministic and inexpensive to execute in itself, proves to be a signal difficult to fake without reproducing exactly, bit by bit, the behavior of the original math library.