Retour au Blog
Comparaison

Protection au niveau du moteur vs au niveau API : pourquoi l'architecture compte

Comparez trois architectures de protection des empreintes numeriques : extensions de navigateur, plugins stealth par injection JS et modification au niveau du moteur. Decouvrez pourquoi seul le controle au niveau du moteur garantit une consistance complete.

Introduction

La protection des empreintes numeriques du navigateur se decline en trois architectures fondamentalement differentes. Chacune opere a une couche distincte de la pile technologique du navigateur, et cette couche determine ce qu'elle peut et ne peut pas controler.

Les trois approches sont :

  1. Les extensions de navigateur qui injectent des scripts apres le chargement de la page pour remplacer les proprietes JavaScript
  2. L'injection JS et les plugins stealth qui modifient l'environnement du navigateur avant l'execution du code de la page, typiquement via des frameworks d'automatisation comme Puppeteer ou Playwright
  3. La modification au niveau du moteur qui change les signaux d'empreinte dans le code compile du navigateur lui-meme, avant que tout contexte JavaScript n'existe

Ce ne sont pas simplement des implementations differentes de la meme idee. Elles sont architecturalement distinctes, et les differences ont des consequences pratiques sur la consistance, la couverture et la fiabilite a long terme. Cet article examine chaque approche en detail, explique pourquoi elles produisent des resultats differents et fournit des conseils pour choisir la bonne architecture selon vos exigences de confidentialite.

Impact sur la vie privee

Une protection des empreintes numeriques incomplete ou inconsistante peut etre pire que l'absence de protection. Lorsque certains signaux sont modifies mais pas d'autres, l'empreinte resultante contient des contradictions. Un navigateur qui pretend fonctionner sous Windows mais produit une sortie Canvas correspondant au rendu Linux est plus distinctif, pas moins. Un navigateur dont la propriete navigator.webdriver a ete remplacee avec Object.defineProperty est identifiable par le fait que le descripteur de propriete ne correspond pas a une implementation native.

Une protection incomplete cree une "empreinte d'outil de confidentialite" unique qui est souvent plus identifiante que l'empreinte originale qu'elle tentait de modifier. Des recherches de plusieurs groupes academiques ont documente cet effet : les utilisateurs de certaines extensions de confidentialite sont plus identifiables que les utilisateurs qui ne prennent aucune precaution.

L'architecture de votre protection determine si vous obtenez une consistance reelle ou si vous creez accidentellement de nouveaux signaux identifiants. Comprendre ces differences architecturales est essentiel pour faire un choix eclaire.

Contexte technique

Approche 1 : Extensions de navigateur

Les extensions de navigateur operent via l'API WebExtensions, qui fournit des scripts de contenu s'executant dans le contexte JavaScript de la page. Une extension de protection d'empreintes fonctionne typiquement en :

  1. Injectant un script de contenu qui s'execute a document_start
  2. Utilisant Object.defineProperty pour remplacer des proprietes comme navigator.hardwareConcurrency, navigator.platform ou screen.width
  3. Enveloppant les APIs Canvas, WebGL et Audio pour modifier leurs valeurs de retour
  4. Interceptant HTMLCanvasElement.prototype.toDataURL et les methodes similaires

Faiblesses de l'approche par extension :

Inconsistance des descripteurs de propriete. Lorsqu'une extension remplace navigator.hardwareConcurrency avec Object.defineProperty, le descripteur de propriete change. Le getter devient une fonction JavaScript plutot que le getter natif du navigateur. Cette inconsistance est visible par tout code s'executant sur la page et constitue une faiblesse bien connue de l'approche par extension.

Modification de la chaine de prototypes. Les extensions qui enveloppent les methodes de prototype laissent des traces dans la chaine de prototypes. Les methodes remplacees sont identifiables comme des fonctions JavaScript non natives plutot que du code integre au navigateur.

Isolation des contextes vierges. C'est la faiblesse la plus fondamentale. Les extensions peuvent ne pas injecter leurs remplacements dans chaque contexte d'execution. Les iframes, Web Workers et autres contextes isoles peuvent acceder aux valeurs originales non modifiees. Si la valeur remplacee dans le cadre principal differe de la valeur originale dans un contexte vierge, l'inconsistance est apparente.

Aucun controle du rendu. Les extensions ne peuvent pas modifier la sortie pixel reelle des operations Canvas, WebGL ou AudioContext. Elles peuvent intercepter les appels API qui lisent la sortie (comme toDataURL ou getImageData), mais ne peuvent pas changer ce que le moteur de rendu produit reellement. Cela signifie que toute approche calculant une empreinte a partir de la sortie de rendu brute, plutot qu'a travers l'API JavaScript, verra les vraies valeurs de l'appareil.

Aucun controle de la couche reseau. Les extensions ne peuvent pas modifier les en-tetes HTTP envoyes lors de la requete de navigation initiale. Les en-tetes Client Hints comme Sec-CH-UA-Platform sont envoyes avant que tout script de contenu puisse s'executer. L'empreinte TLS (JA3/JA4) est completement hors de portee de l'extension.

Approche 2 : Injection JS / Plugins Stealth

Les plugins stealth (comme puppeteer-extra-plugin-stealth) representent une evolution de l'approche par extension. Au lieu de s'appuyer sur l'API des extensions, ils injectent du JavaScript via le framework d'automatisation avant le chargement de la page.

Un plugin stealth typique :

  1. Utilise page.evaluateOnNewDocument() ou page.addInitScript() pour injecter du code avant l'execution de tout JavaScript de la page
  2. Remplace navigator.webdriver, supprime les objets specifiques au framework, patche navigator.plugins et modifie d'autres proprietes detectables
  3. Patche les methodes toString() pour que les fonctions remplacees paraissent natives
  4. Tente de couvrir plusieurs vecteurs de detection simultanement

Ameliorations par rapport aux extensions :

  • Meilleur timing : le code s'execute avant les propres scripts de la page
  • Peut patcher tous les nouveaux contextes en utilisant evaluateOnNewDocument qui s'applique a chaque cadre
  • Peut traiter les signaux specifiques a l'automatisation comme navigator.webdriver et les objets de liaison du framework

Faiblesses restantes :

La frontiere de la couche JavaScript. Les plugins stealth operent entierement dans JavaScript. Ils peuvent remplacer ce que les APIs JavaScript retournent, mais ne peuvent pas controler ce qui se passe en dessous de la couche JavaScript.

Sortie de rendu Canvas et WebGL. Quand un site dessine sur un element Canvas et lit les donnees pixel, le rendu reel est effectue par le pipeline graphique du moteur du navigateur, pas par JavaScript. Un plugin stealth peut intercepter toDataURL() et retourner des donnees modifiees, mais ne peut pas changer le rendu lui-meme. La sortie pixel reelle reste liee au GPU et au pilote reels, creant une inconsistance entre la reponse API interceptee et le rendu sous-jacent.

Empreinte audio. Le traitement AudioContext se fait dans le moteur audio du navigateur. Les plugins stealth peuvent envelopper l'API AudioContext, mais la sortie reelle du traitement audio est determinee par le moteur du navigateur.

Couches HTTP et TLS. Les plugins stealth ne peuvent pas modifier la poignee de main TLS (empreinte JA3/JA4), les en-tetes HTTP de navigation initiale ou les Client Hints envoyes avant l'execution de JavaScript.

Consistance entre les signaux. Les plugins stealth patchent les signaux individuels independamment. S'assurer que tous sont coherents en interne est extremement difficile au niveau JavaScript car le plugin n'a pas acces a l'etat interne du moteur de rendu.

Contextes Worker et SharedWorker. Bien que evaluateOnNewDocument couvre les iframes, les Web Workers et SharedWorkers creent des contextes JavaScript separes qui peuvent ne pas recevoir les scripts injectes.

Approche 3 : Modification au niveau du moteur

La modification au niveau du moteur change le code compile du navigateur lui-meme. Au lieu d'intercepter les appels API JavaScript apres qu'ils ont ete effectues, les valeurs sont definies a la source, dans l'implementation C++ du moteur du navigateur. C'est l'approche de BotBrowser.

Lorsqu'un profil d'empreinte est charge, les valeurs internes du moteur sont configurees avant la creation de tout contexte JavaScript. Quand le code JavaScript appelle navigator.hardwareConcurrency, il passe par le chemin de code normal du moteur et retourne la valeur du profil via le meme getter natif qu'un navigateur standard utiliserait. Pas de remplacement JavaScript, pas de descripteur de propriete modifie, pas de chaine de prototypes alteree.

Ce que le controle au niveau du moteur rend possible :

Descripteurs de propriete natifs. Chaque propriete remplacee a un getter natif car elle est implementee dans le code C++ du moteur. Object.getOwnPropertyDescriptor retourne exactement ce qu'il retournerait sur un navigateur standard. Les appels toString() retournent [native code].

Controle reel du rendu. Les empreintes Canvas, WebGL et audio ne sont pas interceptees au niveau API. Le moteur de rendu lui-meme produit une sortie consistante avec le profil charge.

Consistance de la couche reseau. Les en-tetes HTTP, y compris les Client Hints envoyes lors de la navigation initiale, correspondent au profil.

Couverture uniforme des contextes. Chaque contexte JavaScript, que ce soit dans le cadre principal, un iframe, un Web Worker, un SharedWorker ou un Service Worker, voit les memes valeurs.

Aucune fenetre de timing. Il n'y a aucun moment pendant le chargement de la page ou les vraies valeurs sont visibles.

Trois architectures de protection des empreintes Extension de navigateur JavaScript du site web Script de contenu injecte ici Couche JS API (patchee) Moteur de rendu (non touche) Pile reseau (non touchee) Couche TLS (non touchee) - Descripteurs detectables - Contextes vierges non proteges - Aucun controle du rendu - Aucun controle reseau - Chaine de prototypes modifiee Couverture partielle Injection JS / Plugin Stealth JavaScript du site web evaluateOnNewDocument ici Couche JS API (patchee) Moteur de rendu (non touche) Pile reseau (partielle) Couche TLS (non touchee) - Meilleur timing - Workers possiblement omis - Aucun controle du rendu - Patches toString() detectables - Inconsistance inter-signaux Ameliore, encore des lacunes Modification niveau moteur JavaScript du site web Couche JS API (valeurs natives) Moteur de rendu (controle) Pile reseau (controlee) Couche TLS (controlee) - Descripteurs natifs - Tous les contextes couverts - Sortie de rendu reelle - Consistance reseau complete - Aucune fenetre de timing Couverture complete Controle par la protection Non controle (valeurs reelles exposees) Point d'injection

Tableau comparatif

Le tableau suivant compare les trois approches sur les dimensions cles de protection. C'est une comparaison technique basee sur les capacites architecturales, pas un avis sur des produits specifiques.

Dimension de protectionExtension de navigateurPlugin StealthNiveau moteur
Proprietes navigatorRemplacement JS (descripteur detectable)Remplacement JS (meilleur timing)Valeurs natives C++
Empreinte CanvasInterception API uniquementInterception API uniquementSortie de rendu controlee
Empreinte WebGLInterception API uniquementInterception API uniquementSortie de rendu controlee
Empreinte audioInterception API uniquementInterception API uniquementSortie de traitement controlee
Empreinte des policesNe controle pas la disponibiliteNe controle pas la disponibiliteListe de polices du profil
En-tetes HTTPNe modifie pas la requete initialePartiel (post-navigation uniquement)Definis au niveau de la pile reseau
Client HintsAucun controleAucun controleControles par profil
Empreinte TLS (JA3/JA4)Aucun controleAucun controleControlee par le moteur
Contextes iframe/WorkerPeut manquer les contextes viergesCouvre les iframes, peut manquer les WorkersTous les contextes uniformes
Verification des descripteursDetectable (getter JS)Detectable (getter JS)Natif (indiscernable)
Integrite de la chaine de prototypesModifieeModifieeNon modifiee
Fenetre de timingApres le debut du chargementAvant le JS de la page, apres init du moteurAucune (actif des le demarrage)
Consistance inter-signauxPatches independantsPatches independantsPilote par le profil, unifie
Surcharge de performanceInjection de script par pageInjection de script par pageZero surcharge a l'execution

L'approche au niveau du moteur de BotBrowser

BotBrowser implemente la protection des empreintes numeriques au niveau du moteur du navigateur. Voici un resume des capacites specifiques que cette architecture permet.

Systeme de profils

Chaque empreinte est definie par un profil capture lors d'une session de navigateur reelle sur du materiel reel. Le profil contient l'ensemble complet des signaux de l'appareil : proprietes navigator, dimensions d'ecran, informations GPU, listes de polices, caracteristiques de rendu, parametres de traitement audio et plus encore. Charger un profil configure tous ces signaux simultanement, garantissant la coherence interne.

# Charger un profil capture d'une session reelle Windows Chrome
chrome --bot-profile="/opt/profiles/windows-chrome-134.enc" \
       --user-data-dir="$(mktemp -d)"

Graine de bruit deterministe

Pour les scenarios de recherche et de test necessitant des resultats reproductibles, BotBrowser supporte une graine de bruit qui controle tous les signaux d'empreinte aleatoires :

chrome --bot-profile="/opt/profiles/profile.enc" \
       --bot-noise-seed=42

Le meme profil avec la meme graine produit des hachages Canvas identiques, une sortie WebGL et des empreintes audio identiques a chaque execution, quel que soit le systeme d'exploitation ou le materiel hote. Ceci est precieux pour les tests de regression, les pipelines CI/CD et les experiences controlees.

Empreintes par contexte

BotBrowser supporte l'execution de multiples identites isolees au sein d'un seul processus de navigateur. Chaque contexte de navigateur peut avoir son propre profil d'empreinte, proxy et parametres geographiques.

Avec la configuration par contexte, une seule instance de navigateur peut executer 50 identites independantes. Les donnees de reference montrent que cette approche atteint 29% d'economie de memoire et 57% de reduction du nombre de processus par rapport a l'execution de 50 instances de navigateur separees.

Consistance multiplateforme

Un profil Windows charge sur un serveur Linux produit une sortie consistante Windows sur chaque signal : proprietes navigator, listes de polices, rendu Canvas, sortie WebGL, en-tetes HTTP et Client Hints. Le systeme d'exploitation hote est invisible. Ceci est architecturalement impossible avec les approches par extension ou injection JS car elles ne peuvent pas controler le moteur de rendu ni la pile reseau.

Performance

La protection au niveau du moteur n'a effectivement aucune surcharge a l'execution car il n'y a pas d'injection JavaScript par page, pas d'interception d'API et pas de patchage a l'execution. Les valeurs du profil sont compilees dans le chemin d'execution du navigateur.

Resultats de performance :

  • Speedometer 3.0 : BotBrowser obtient 42.7 contre 42.8 pour Chrome standard (difference de 0.2%)
  • Latence des APIs Canvas/WebGL/Navigator/Screen/Font : 0ms de delai supplementaire
  • Aucun cout d'injection par page : contrairement aux extensions et plugins stealth, il n'y a pas de JavaScript a injecter et executer a chaque chargement de page

Exemples d'integration

Integration Playwright

const { chromium } = require('playwright-core');

(async () => {
  const browser = await chromium.launch({
    executablePath: '/opt/botbrowser/chrome',
    args: [
      '--bot-profile=/opt/profiles/profile.enc',
    ],
    headless: true,
  });

  const context = await browser.newContext({ viewport: null });
  const page = await context.newPage();

  await page.goto('https://abrahamjuliot.github.io/creepjs/');

  // Tous les signaux d'empreinte sont consistants avec le profil charge.
  // Aucun plugin stealth necessaire. Aucun patch evaluateOnNewDocument.
  // Canvas, WebGL, audio, polices, navigator - tout controle au niveau du moteur.

  const fingerprint = await page.evaluate(() => ({
    platform: navigator.platform,
    hardwareConcurrency: navigator.hardwareConcurrency,
    webdriver: navigator.webdriver,
    descriptorType: typeof Object.getOwnPropertyDescriptor(
      Navigator.prototype, 'hardwareConcurrency'
    ).get,
  }));

  console.log(fingerprint);
  await browser.close();
})();

Integration Puppeteer

const puppeteer = require('puppeteer-core');

(async () => {
  const browser = await puppeteer.launch({
    executablePath: '/opt/botbrowser/chrome',
    args: [
      '--bot-profile=/opt/profiles/profile.enc',
      '--bot-disable-console-message',
    ],
    headless: true,
    defaultViewport: null, // Preserver les dimensions d'ecran du profil
  });

  const page = await browser.newPage();
  await page.goto('https://example.com');

  // Verifier la consistance : cadre principal et iframe retournent des valeurs identiques
  const consistency = await page.evaluate(() => {
    const iframe = document.createElement('iframe');
    iframe.srcdoc = '<html></html>';
    document.body.appendChild(iframe);

    return {
      mainPlatform: navigator.platform,
      iframePlatform: iframe.contentWindow.navigator.platform,
      match: navigator.platform === iframe.contentWindow.navigator.platform,
    };
  });

  console.log('Consistance inter-contextes :', consistency);
  // match: true (garanti par le controle au niveau du moteur)

  await browser.close();
})();

Configuration de production

chrome \
  --bot-profile="/opt/profiles/windows-chrome-134.enc" \
  --proxy-server=socks5://user:pass@proxy.example.com:1080 \
  --bot-disable-debugger \
  --bot-disable-console-message \
  --bot-always-active \
  --bot-inject-random-history \
  --bot-port-protection \
  --user-data-dir="/data/session-1" \
  --headless

Verification : comment tester votre protection

Quel que soit l'approche que vous utilisez, vous devez verifier l'efficacite de votre protection. Voici les verifications cles :

1. Verification des descripteurs de propriete

const checks = await page.evaluate(() => {
  const props = [
    'hardwareConcurrency', 'deviceMemory', 'platform',
    'languages', 'webdriver'
  ];

  return props.map(prop => {
    const desc = Object.getOwnPropertyDescriptor(Navigator.prototype, prop);
    return {
      property: prop,
      hasNativeGetter: desc?.get?.toString().includes('[native code]') ?? 'no getter',
      value: navigator[prop],
    };
  });
});
console.log('Verification des descripteurs :', checks);
// Niveau moteur : tous affichent [native code]
// Extension/stealth : affiche une fonction JavaScript

2. Consistance inter-contextes

const crossContext = await page.evaluate(() => {
  const iframe = document.createElement('iframe');
  iframe.style.display = 'none';
  document.body.appendChild(iframe);

  const signals = ['platform', 'hardwareConcurrency', 'deviceMemory', 'languages'];
  const results = {};

  for (const signal of signals) {
    const mainValue = JSON.stringify(navigator[signal]);
    const iframeValue = JSON.stringify(iframe.contentWindow.navigator[signal]);
    results[signal] = {
      main: mainValue,
      iframe: iframeValue,
      consistent: mainValue === iframeValue,
    };
  }

  document.body.removeChild(iframe);
  return results;
});
console.log('Inter-contextes :', crossContext);

3. Outils de verification en ligne

Naviguez vers ces sites et verifiez s'il y a des avertissements d'inconsistance :

  • CreepJS - Analyse complete des empreintes avec detection de mensonges
  • BrowserLeaks - Tests de signaux individuels (Canvas, WebGL, polices, etc.)

Questions frequentes

Les plugins stealth peuvent-ils atteindre le meme resultat que la modification au niveau du moteur ?

Non. Les plugins stealth operent dans la couche JavaScript et ne peuvent pas controler la sortie de rendu, les en-tetes au niveau reseau, les empreintes TLS ou le comportement des descripteurs de propriete des valeurs remplacees. Ce sont des limitations architecturales, pas des lacunes d'implementation qui pourraient etre corrigees avec un meilleur code.

Ai-je encore besoin de plugins stealth si j'utilise BotBrowser ?

Non. Les plugins stealth sont redondants avec BotBrowser et peuvent introduire leurs propres artefacts detectables (le JavaScript injecte lui-meme peut etre detecte). BotBrowser gere tous les signaux que les plugins stealth adressent, plus les signaux qu'ils ne peuvent pas atteindre.

Qu'en est-il des extensions de navigateur qui pretendent randomiser Canvas ?

La randomisation Canvas au niveau de l'extension intercepte les appels API toDataURL() et getImageData() et ajoute du bruit a la sortie. Cette approche a deux problemes. Premierement, le bruit n'est pas applique au rendu reel, donc la sortie pixel sous-jacente reste inchangee independamment de l'interception API. Deuxiemement, le bruit aleatoire produit une empreinte qui change a chaque chargement de page, ce qui est en soi un signal identifiant. Les appareils reels produisent une sortie Canvas consistante.

La modification au niveau du moteur affecte-t-elle les performances du navigateur ?

De maniere negligeable. Le score Speedometer 3.0 de BotBrowser est de 42.7 contre 42.8 pour Chrome standard, une difference de 0.2%. Il n'y a pas de cout d'injection par page ni d'interception d'API a l'execution. Les valeurs du profil font partie du chemin d'execution normal du navigateur.

Comment BotBrowser gere-t-il les nouvelles techniques de fingerprinting ?

Parce que BotBrowser opere au niveau du moteur, les nouveaux vecteurs de fingerprinting qui lisent des valeurs du moteur du navigateur (nouvelles APIs, nouvelles techniques de rendu, nouveaux types d'en-tetes) peuvent etre adresses en mettant a jour le code du moteur. C'est different de l'approche extension/plugin, ou chaque nouveau vecteur necessite un nouveau patch JavaScript qui peut avoir ses propres problemes de detectabilite.

Puis-je utiliser BotBrowser avec Selenium ?

BotBrowser fonctionne au mieux avec Playwright et Puppeteer, qui communiquent via CDP (Chrome DevTools Protocol). Selenium utilise le protocole WebDriver, qui peut introduire des signaux d'automatisation supplementaires. Si vous utilisez Selenium, BotBrowser controle toujours les signaux d'empreinte au niveau du moteur, mais certains artefacts specifiques a Selenium peuvent ne pas etre adresses.

La modification au niveau du moteur est-elle plus difficile a configurer que les plugins stealth ?

Non. La configuration est en fait plus simple. Au lieu d'installer des packages de plugins stealth, de configurer de multiples options de patchage et d'esperer qu'ils n'entrent pas en conflit, vous pointez votre framework d'automatisation vers le binaire BotBrowser et specifiez un profil. Un binaire, un profil, protection complete.

Resume

L'architecture de votre protection des empreintes numeriques determine son efficacite. Les extensions de navigateur et les plugins stealth operent au niveau de l'API JavaScript, ce qui signifie qu'ils ne peuvent qu'intercepter les appels API, pas controler les signaux sous-jacents. La modification au niveau du moteur controle les signaux a leur source, produisant une sortie consistante et authentique sur chaque API, chaque contexte et chaque couche de la pile du navigateur.

BotBrowser est open source et disponible sur GitHub : https://github.com/botswin/BotBrowser

Articles connexes

#fingerprint protection#browser engine#stealth plugin#browser extension#privacy#architecture comparison#consistency#puppeteer#playwright