À partir de Chromium 148, il est possible d’identifier l’OS sous-jacent via Math.tanh
(scrapfly.dev)- À partir de Chrome 148, le
Math.tanhde V8 appelle lestd::tanhde l’hôte au lieu du fdlibm intégré, si bien que pour une même entrée, la glibc de Linux, lelibsystem_mde macOS et l’UCRT de Windows renvoient un dernier bit différent Math.tanh(0.8)vaut0.6640367702678491sur Linux,0.664036770267849sur macOS et0.6640367702678489sur Windows, ce qui permet de distinguer les trois OS en un seul appel ; si le résultat ne correspond pas à l’OS revendiqué par le User-Agent, le camouflage est démasqué- Les chemins de fuite diffèrent selon les moteurs : dans les
Math.*de V8, seultanhutilise la bibliothèque mathématique de l’hôte, mais dans Blink, l’ensemble des fonctions trigonométriques CSS ainsi que certains calculs de Web Audio passent aussi par des bibliothèques dépendantes de l’OS - Introduire des variations arbitraires dans la valeur ne correspond à aucun OS réel et casse même le déterminisme ; il faut donc reproduire au bit près les coefficients, tables, réductions de domaine et comportements FMA de la bibliothèque visée, ou mapper directement le code UCRT d’origine
- À chaque release, Scrapfly vérifie la correspondance bit à bit de
Math.tanhet de 7 fonctions trigonométriques CSS sur 871 000 entrées en les comparant à de vrais Mac et Chrome, et aligne non seulement la précision mais aussi les différences d’architecture et les temps d’exécution sur ceux d’un vrai navigateur
L’OS révélé par Math.tanh
- Le résultat de
Math.tanh(0.8)varie selon la bibliothèque mathématique de l’hôte- glibc de Chrome sur Linux :
0.6640367702678491 libsystem_mde Chrome sur macOS :0.664036770267849- UCRT de Chrome sur Windows :
0.6640367702678489
- glibc de Chrome sur Linux :
- Apple et glibc présentent en général un écart de 1 ULP sur environ un quart de toutes les entrées, tandis que l’UCRT de Windows diffère des deux autres bibliothèques sur quelques pourcents des entrées
- ULP (unit in the last place) est l’écart entre deux nombres à virgule flottante consécutifs représentables à une grandeur donnée ; 1 ULP est la plus petite différence qu’un
doublepeut représenter
- ULP (unit in the last place) est l’écart entre deux nombres à virgule flottante consécutifs représentables à une grandeur donnée ; 1 ULP est la plus petite différence qu’un
- Mesuré sur un vrai Chrome 150 via le DevTools Protocol sous Linux, macOS 26 sur Apple Silicon et Windows 11, le pouvoir discriminant varie selon l’entrée
tanh(0.5)donne0.46211715726000974sur les trois OS, donc inutile pour la détectiontanh(0.7)ne diffère que sur Linux, d’1 ULPtanh(0.8)diffère sur les trois OS, avec une plage totale de 2 ULPtanh(0.9)ne diffère que sur Windows, d’1 ULP
- Pour environ trois quarts des entrées, les trois OS produisent le même résultat, mais une seule entrée bien choisie suffit pour obtenir une signature par OS
- Si un navigateur prétend être sur macOS tout en renvoyant les bits mathématiques de Linux, le résultat de
Math.tanhcontredit le User-Agent
Le changement introduit dans Chrome 148
- Jusqu’à Chrome 147, V8 embarquait un port de fdlibm, une implémentation mathématique portable, pour calculer
Math.tanh, ce qui renvoyait les mêmes bits sur tous les OS - Le commit V8
c1486295ae5a remplacé l’implémentation embarquée par lestd::tanhde la plateforme- Ce changement est apparu pour la première fois dans V8 14.8.57 et Chrome 148
- Chrome 148, 149 et 150 exposent les différences du
libmhôte, alors que Chrome 147 et antérieurs ne divulguent pas l’OS par ce chemin
- La norme IEEE 754 définit le stockage des
double, mais n’impose pas que les fonctions transcendantes commesin,cos,tanhouexpsoient correctement arrondies dans tous les cas - Chaque bibliothèque mathématique (
libm) de système fait un compromis entre performance et erreur en ULP, et utilise des coefficients de polynômes minimax, des tables de lookup et des constantes de réduction de domaine différents- Linux utilise glibc
- macOS utilise le
libsystem_md’Apple - Windows utilise
ucrtbase.dllde l’UCRT
- Un détecteur n’a pas besoin d’analyser l’opération mathématique elle-même : il peut simplement comparer les résultats par entrée avec une table issue de vrais Chrome
Quatre pièges qui rendent la reproduction difficile
-
Seules certaines fonctions de V8 fuient
- V8 lie statiquement la plupart de ses implémentations mathématiques, ce qui produit les mêmes résultats quel que soit l’OS
Math.exp,Math.pow,Math.atanet d’autres utilisent des implémentations llvm-libc embarquéesMath.sinetMath.cosutilisent des routinesdbl-64embarquées dérivées de glibc- Depuis Chrome 148, seul
Math.tanh, parmi lesMath.*, divulgue l’OS via lestd::tanhde la plateforme - Si l’on falsifie aussi des fonctions qui ne fuient pas pour les faire ressembler à l’OS cible, on ne reproduit plus le vrai graphe d’appels de V8 ; le fait que seul
tanhdiffère est lui-même vérifiable
-
JavaScript et CSS empruntent des chemins différents
- Les fonctions CSS
sin(),cos(),atan2()ne partagent pas le code deMath.sinen JavaScript - Le moteur de rendu Blink réduit d’abord les angles en degrés, puis appelle le
std::sinde la plateforme, etc., sur la valeur réduite - Le résultat diffère d’un calcul direct avec une entrée en radians, et les 7 fonctions trigonométriques CSS divulguent l’OS via le
libmhôte - Une reproduction bit à bit doit inclure non seulement la fonction mathématique finale, mais aussi la réduction de domaine en degrés et la conversion radians/degrés
- Les fonctions CSS
-
Il existe aussi deux bibliothèques distinctes à l’intérieur de macOS
- Sur Apple Silicon, on trouve à la fois le
libsystem_mscalaire et les routines vectorielles Accelerate commevvsinetvvtanh, et ces deux implémentations ne sont pas identiques - Sur 1 million d’entrées, selon la fonction, 10 à 89 % des résultats différaient
cos(0)vaut exactement1.0dans l’implémentation scalaire- Dans Accelerate, il renvoie
0.9999999999999999 - En mesurant le vrai Chrome sur Mac via le protocole de débogage, on distingue quelle bibliothèque est appelée à chaque point d’appel
Math.tanh, les fonctions trigonométriques CSS et les fonctions transcendantes échantillon par échantillon du compresseur audio utilisent lelibsystem_mscalaire- Le DSP Web Audio, la FFT, les maths vectorielles et les filtres biquad sur Mac utilisent Accelerate
- Les chemins Chromium concernés incluent
fft_frame_mac.cc,vector_math_mac.h,biquad.ccetBUILDFLAG(IS_MAC) - Choisir la mauvaise bibliothèque Apple selon le point d’appel peut introduire un écart de 1 ULP sur la majorité des entrées
- Sur Apple Silicon, on trouve à la fois le
-
L’architecture CPU influe aussi sur le résultat
- ARM et x86 diffèrent sur le fused multiply-add (FMA) et sur la propagation du signe des NaN
- Même si la procédure mathématique est correcte, si le compilateur fusionne multiplication et addition sur une seule architecture, les bits du résultat changent
Chemins de fuite selon le moteur et la fonctionnalité
- Les
Math.*JavaScript de V8 utilisent presque tous des implémentations embarquées, et seulMath.tanhse connecte aulibmhôtesin,cos,tan,asin,acos,atan,atan2,exp,log,log2,log10,powutilisent des implémentations embarquées de V8sqrt,abset les quatre opérations arithmétiques sont des opérations matérielles
- Les fonctions mathématiques de
calc()en CSS appellent directement la bibliothèque de plateforme depuis Blinksin,cos,tan,asin,acos,atan,atan2,exp,log,log2,log10,powutilisent lelibmhôte- Il n’existe pas de chemin CSS équivalent pour
tanh
- Web Audio mélange plusieurs implémentations selon le point d’appel
- Sur Mac, la FFT d’oscillateur, les additions/multiplications/mises à l’échelle vectorielles et la FFT utilisent
vDSPd’Accelerate - Les fonctions transcendantes échantillon par échantillon de DynamicsCompressor, comme
sin,exp,log10f,powf, utilisent lelibsystem_mscalaire - Un même graphe audio peut ainsi s’étendre sur trois bibliothèques : les maths embarquées de V8, la bibliothèque scalaire et Accelerate
- Sur Mac, la FFT d’oscillateur, les additions/multiplications/mises à l’échelle vectorielles et la FFT utilisent
- WebAssembly ne possède pas d’instruction pour les fonctions transcendantes
- Le résultat de
sin, etc., dépend donc dulibmembarqué dans le module - Les opérations arithmétiques comme
f64.sqrtetf64.muls’exécutent sur le matériel et restent identiques selon l’OS - Il reste comme axe d’empreinte la normalisation des NaN entre ARM et x86, ainsi que certaines différences d’arrondi SIMD
- Le résultat de
- Les signaux de détection se concentrent sur
Math.tanh, toutes les fonctions trigonométriques CSS et Web Audio- La FFT Accelerate de Web Audio révèle l’architecture CPU
- Le
libsystem_mscalaire du compresseur révèle l’OS
Reproduire exactement plutôt que perturber les valeurs
-
Pourquoi le bruit échoue
- Ajouter du bruit au résultat peut aboutir à une valeur qui ne correspond à aucun OS réel dans la table de référence
- Si la valeur aléatoire change à chaque appel, le déterminisme est rompu, ce qui constitue en soi un autre signal de détection
- L’objectif n’est pas d’obtenir une valeur proche, mais le même résultat bit à bit que celui renvoyé par l’OS revendiqué
-
Restaurer tous les éléments de l’algorithme cible
- Les coefficients d’approximation minimax, les tables d’exposants et les constantes de réduction de domaine du
libmcible sont récupérés puis portés dans un code C portable - Il faut aussi reproduire à l’identique les entrées pour lesquelles la bibliothèque cible arrondit dans la « mauvaise » direction
- La reproduction de
sinchez Apple utilise le motif exact de bits des coefficients extraits delibsystem_mainsi que des appels explicites àfma() - Si l’on retranscrit les coefficients en décimal, ils peuvent être réarrondis pendant la transcription ; ils sont donc conservés en flottants hexadécimaux
- Chaque multiplication-addition fusionnée par Apple est explicitement fusionnée dans le code également
- Les coefficients d’approximation minimax, les tables d’exposants et les constantes de réduction de domaine du
-
Verrouiller le FMA de manière déterministe
- La compilation avec
-ffp-contract=offempêche le compilateur d’ajouter ou de supprimer arbitrairement des FMA - Seuls les
fma()explicitement présents dans le code s’exécutent aux mêmes endroits que chez Apple, ce qui permet d’obtenir les mêmes bits sur un serveur x86 tout en imitant ARM - Le FMA matériel et un FMA logiciel correctement arrondi renvoient les mêmes bits
- La compilation avec
Utiliser le code d’origine de l’UCRT de Windows
- L’UCRT de Windows utilise la même ISA x86-64 que les serveurs Linux et est indépendant de la position, ce qui permet de mapper le vrai
ucrtbase.dllen mémoire à l’exécution et d’appeler directement les exports des fonctions mathématiques - Comme c’est le code d’origine qui s’exécute, on obtient les vrais bits UCRT sans avoir à rétroconcevoir un algorithme mathématique séparé
- Il faut gérer les différences entre l’ABI System V de Linux et l’ABI x64 de Windows
- En x64 Windows, l’appelé utilise un shadow space de 32 octets au-dessus de l’adresse de retour
- L’ensemble des registres préservés par l’appelé diffère aussi de System V
- Si le pointeur de fonction n’est pas déclaré avec
ms_abi, l’écriture dans le shadow space peut corrompre la stack frame générée par clang et faire sauter l’appel indirect vers une mauvaise adresse
- Le code du DLL mappé n’est pas une cible d’appel indirect enregistrée dans la CFI
- En production,
-fsanitize=cfi-icallpeut déclencher un trap#UDet unSIGILLà chaque appel - Le wrapper qui appelle le pointeur de fonction a besoin de
clang::no_sanitize("cfi-icall")
- En production,
- Les fonctions mathématiques de l’UCRT lisent en préambule un drapeau de dispatch CPU via
mov eax, [rip+disp32]pour choisir entre un chemin scalaire ou un chemin FMA/AVX2- Dans un DLL fraîchement mappé, ce drapeau vaut 0 et sélectionne donc le chemin scalaire plus lent
- Les bits du résultat sur ce chemin diffèrent de ceux d’un système Windows moderne
- Il faut localiser l’adresse du drapeau dans le prologue de
tanhet la forcer vers le chemin FMA avant le premier appel pour obtenir une correspondance bit à bit avec un vrai Windows
Emplacement du patch et contraintes de performance
- On hooke le goulot d’étranglement unique où le moteur appelle
libm, puis on choisit le chemin selon l’OS revendiqué par le navigateur- S’il revendique Linux, on conserve glibc
- S’il revendique macOS, on utilise l’implémentation de reproduction Apple
- Même si le résultat est exact, un temps d’exécution différent de celui d’un vrai navigateur peut être détecté
- La première build utilisait une baseline x86 trop ancienne pour le FMA matériel, abaissant tous les
fma()en appels logiciels ; elle était 2,5 à 6 fois plus lente que le natif - En comparant le ratio de temps d’exécution entre des boucles
Math.tanhetMath.sin, on peut révéler un motif de performance absent d’un vrai navigateur - Une fois le FMA matériel activé, chaque opération fusionnée devient une seule instruction, ce qui donne un gain d’environ 6× ; c’était alors plus rapide que glibc tout en conservant des bits identiques
Validation sur 871 000 entrées
- Le harnais de validation exécute 871 000 entrées à chaque release sur toutes les branches et l’ensemble du domaine
- grille d’entrées dense
- frontières d’intervalle
- nombres subnormaux
- zéros signés
- infinis
- NaN
- Deux types d’environnements réels servent de référence
- Un vrai Mac calcule séparément, pour toutes les entrées, les résultats scalaire et Accelerate afin d’identifier les points où les deux implémentations divergent
- Un vrai Chrome sur Mac est piloté via le protocole de débogage pour collecter les résultats pleine précision de
Math.tanhet de toutes les fonctions trigonométriques CSS
Math.tanhet les fonctions CSSsin,cos,tan,asin,acos,atan,atan2correspondent bit à bit à un vrai Chrome sur Mac- La validation vérifie aussi que l’implémentation de reproduction se comporte comme le vrai code machine inclus dans le binaire distribué
- Il faut également reproduire le post-traitement du navigateur aux frontières du domaine
- Sur un vrai Mac,
asin(2)en CSS est hors domaine, devient NaN, puis CSS borne NaN à 0, donc la valeur finale est0 - Une implémentation de reproduction simpliste pourrait à tort renvoyer 90 degrés
- Sur un vrai Mac,
Pourquoi les maths sont cruciales dans le camouflage de navigateur
- Les résultats mathématiques sont déterministes et peu coûteux à vérifier, mais les imiter correctement exige de connaître le fonctionnement interne des
libmdes éditeurs et les chemins d’appel propres à chaque moteur - Pour correspondre à un vrai navigateur, il faut déterminer quelle bibliothèque mathématique V8, Blink et Web Audio choisissent à chaque point d’appel, puis reproduire jusqu’au dernier bit, le comportement selon l’architecture et même les temps d’exécution
- Le Scrapium de Scrapfly est configuré, lorsqu’on lui demande de se présenter comme macOS, pour faire correspondre au trafic réel de macOS jusqu’au bit d’arrondi d’un cosinus
1 commentaires
Réactions sur Hacker News
L’explication selon laquelle un seul appel à
tanhavec l’entrée de droite produit une signature propre au système d’exploitation passe à côté de la possibilité d’identifier une plage de versions du navigateurLa plupart des gens n’usurpent pas le système d’exploitation de leur User-Agent, et le fingerprinting s’intéresse davantage à des combinaisons de caractéristiques quasi uniques qu’au système d’exploitation lui-même. La découverte est intéressante, mais l’article paraît trop rédigé par un LLM, ce qui nuit à sa crédibilité
Cela leur permet de passer plus facilement la détection de bots et de vendre à leurs clients des données collectées sur d’autres sites web
Le fait que le texte a été écrit avec un LLM a été divulgué dans l’article et sur le blog ; rien n’a été caché et personne n’a prétendu être humain. Faute de temps, l’article n’aurait probablement pas été publié autrement, et ce choix est assumé
Les navigateurs ajoutent sans cesse des fonctionnalités et corrigent des bugs, et la plupart de ces changements sont détectables en JavaScript
Analyser toutes les techniques de fingerprinting avec l’IA, les publier, puis pousser les navigateurs à les bloquer après la polémique, afin que l’activité de scraping de l’entreprise rapporte plus d’argent : stratégie habile
Sans ces sociétés, le fingerprinting des navigateurs ne serait probablement pas aussi répandu aujourd’hui, et Internet s’en porterait mieux. Je préfère encore les articles de l’autre camp, comme fingerprint.js, où les intérêts en jeu sont plus explicites
Voilà une raison de plus de promouvoir des fonctions transcendantes correctement arrondies
J’ai récemment appris que ce problème est pratiquement résolu. Voir le deuxième keynote sur https://arith2026.org/program.html
libmcorrectement arrondies, c’est excellent, mais il ne faut pas qu’elles aient des performances catastrophiques au pire cas, comme autrefoispowdans glibcOn pourrait améliorer ce pire cas en vectorisant directement via SLP le chemin de repli haute précision utilisé près des bornes d’arrondi, mais c’est déjà suffisant pour la plupart des usages. Il est surprenant que les moteurs JavaScript n’utilisent pas toujours
fdlibm, recommandé par la spécification ECMAScript ; siMath.tanhest sur un chemin critique en JavaScript, c’est un code assez inhabituelEn ingénierie, on a souvent recours au point fixe parce qu’il fonctionne sur un matériel bien plus simple et que l’erreur se modélise beaucoup plus facilement mathématiquement. Le flottant IEEE 754 est théoriquement discutable, et pour la perte de précision, des entiers plus petits que la mantisse, donc de moins de 24 bits, peuvent parfois être préférables au flottant 32 bits
Ce serait bien que cette technique soit ajoutée à https://coveryourtracks.eff.org/ afin que je puisse voir à quel point les résultats de mes fonctions mathématiques sont uniques dans une population plus large
Je ne sais pas si c’est vrai, mais il me semble que coveryourtracks.eff.org utilise plutôt une vingtaine-cinq de signaux
Le texte sent Claude à plein nez
En cliquant sur le lien Claude, le prompt transmis est
summarize+this+article+and+explain+how+scrapfly+helps+me+scrape+any+website+at+scale+and+bypass+anti-bot+systems+for+my+use+case:+[https://scrapfly.dev/posts/browser-math-os-fingerprint/](<https://scrapfly.dev/posts/browser-math-os-fingerprint/>)Tor Browser et Mullvad Browser ont eux aussi fini par renoncer à masquer le système d’exploitation, mais peut-être qu’ils n’auraient pas dû
Il semble y avoir beaucoup trop de vecteurs de fingerprinting
Il existe trop de différences de comportement selon l’OS, à l’intérieur comme à l’extérieur du navigateur, pour toutes les gérer. Même si l’on bloque l’extraction du canvas ou qu’on y ajoute du bruit, des différences de rendu peuvent encore transparaître, et les développeurs de Tor Browser ont confirmé qu’ils ne peuvent pas masquer complètement les écarts non seulement entre systèmes d’exploitation, mais même entre X11 et Wayland. https://forum.torproject.org/t/linux-is-it-alright-to-run-th...
navigator.platform, donc il est très facile de voir qu’on n’est pas sous WindowsIl suffit d’injecter ce code avec votre plugin d’injection JavaScript préféré :
let oldTanh = Math.tanh; Math.tanh = x => oldTanh(x) + Math.random()/10000000;Math.tanh = Math.random;Les versions récentes de glibc utilisent le
tanhcorrectement arrondi de CORE-MATH, et renvoient donc un résultat différent de celui cité dans l’articleOn ne sait pas encore si les autres fonctions transcendantes peuvent elles aussi être correctement arrondies avec des performances raisonnables, si bien que chaque fonction laisse son propre fingerprint
Chrome contient déjà des centaines de Mo de code exécutable ; je pensais qu’il liait statiquement à peu près la moitié des bibliothèques de l’espace utilisateur
Je pensais aussi que
tanhn’était pas un appel de fonction, mais une opération intégrée émise par le JIT JavaScript sous forme d’instruction CPU, donc l’idée de bifurquer versdlsym()pour une opération mathématique paraît étrange. Les instructions CPU elles-mêmes peuvent d’ailleurs aussi servir au fingerprintingLe microcode ne bénéficie pas d’avantages comme la prédiction de branchement, et se révèle en pratique plus lent qu’une implémentation logicielle
Je me demande s’il est possible de gagner cette bataille
En exécutant suffisamment de fonctions, on pourrait sans doute combiner les ratios de temps d’exécution et les résultats d’arrondi pour déduire non seulement le système d’exploitation et le modèle exact de machine, mais aussi d’autres tâches en cours sur le même appareil. On ne peut probablement pas l’empêcher complètement ; au mieux, on peut juste rendre la chose un peu plus difficile
Au final, c’est à la société et au droit de rattraper leur retard. De la même manière qu’une serrure n’empêche pas totalement les intrusions mais que la réprobation sociale et les sanctions pénales complètent sa protection, il faudrait rendre illégal ce type de traçage individuel et mettre au ban les entreprises qui en profitent ainsi que ceux qui y travaillent
Dans des pays comme la Russie, la Birmanie ou la Corée du Nord, l’État de droit ne fonctionne pas, et les autorités locales protègent parfois activement des criminels qui escroquent des étrangers ; l’analogie avec la serrure ne tient donc pas