Le système de détection des bots de TRACIO identifie les navigateurs automatisés, les outils headless et les attaques scriptées grâce à une architecture à deux couches : collecte de signaux côté client et analyse côté serveur. Il détecte les frameworks d'automatisation courants, les navigateurs headless, les agents de navigation IA et les navigateurs anti-détection avec des faux positifs quasi nuls, tout en plaçant automatiquement en allowlist les crawlers légitimes des moteurs de recherche.
L'agent du navigateur collecte une série de signaux spécifiques aux bots et de validation croisée qui alimentent les décisions côté serveur. Les ID de signaux ci-dessous sont donnés à titre indicatif — les ID internes ne constituent pas un contrat public stable :
| Signal | Description |
|---|---|
s300 (webdriver) | Flag navigator.webdriver — mis à true par les frameworks d'automatisation |
s301 (eval length) | eval.toString().length — Chrome/Edge : 33, Firefox : 37. D'autres valeurs indiquent une instrumentation. |
s305 (bot composite) | Analyse window/document à la recherche de 14 artefacts de frameworks d'automatisation |
s97 (headless markers) | Artefacts CDP, permissions de notification, nombre de plugins, dimensions extérieures |
s157 (automation scan) | Analyse exhaustive pour Selenium, Puppeteer, PhantomJS, Playwright et 10 autres |
s158 (iframe webdriver) | Crée une iframe cachée et lit navigator.webdriver à l'intérieur. Attrape les outils qui patchent le frame principal mais oublient les iframes. |
s159 (native function) | Supprime Function et Object, teste si les getters sont réellement natifs. Détecte l'instrumentation basée sur Proxy. |
s155 (window CRC32) | Analyse par hash CRC32 de chaque propriété de window pour détecter les globales d'automatisation injectées |
s163 (devtools trap) | Piège de getter de console — détecte quand les DevTools sont activement ouvertes |
Le serveur exécute un ensemble de détecteurs dont les sorties pondérées se combinent en un
score de bot. Le score est mappé sur un verdict via des seuils — bot lorsqu'il est ≥ 0.70,
suspicious lorsqu'il est ≥ 0.30, sinon human — et un signal à échec forcé (comme
navigator.webdriver) impose directement un verdict bot. Les verdicts de bot portent une
étiquette bot_type de forme libre.
Détecteurs représentatifs et leur effet :
| Détecteur | Effet |
|---|---|
| Flag webdriver | Échec forcé → bot (type webdriver) |
| Analyse composite d'automatisation / framework | Poids élevé → bot (type {framework}) |
| Marqueurs headless | Pondéré → bot (type headless) |
| Anomalie d'eval-length | Pondéré → bot (type unknown) |
| Motif d'UA de bot malveillant | Pondéré → bot (type {label}) |
| Bon bot vérifié (DNS inverse) | Allowlist → human |
| Comportemental (débit / entropie / interaction) | Pondéré → bot (type rateBot) |
| VM (Enterprise) · Émulateur · DevTools (Business+) | Signaux pondérés d'appareil/altération |
Niveau et disponibilité. La détection des DevTools s'exécute sur Business et Enterprise ; la détection de VM est réservée à Enterprise ; la détection d'émulateur s'exécute sur tous les plans. Toutes fonctionnent dans l'agent web. La détection de root, jailbreak, apps clonées/dupliquées, Frida et réinitialisation d'usine dépend de signaux mobiles natifs et n'est pas efficace dans le déploiement web uniquement (en développement). Appuyez-vous sur les verdicts d'automatisation, headless, agent IA, anti-détection, VM/émulateur et comportementaux, qui sont pleinement actifs sur le web.
La détection des bots écrit un champ bot_result sur chaque événement (et bot.result dans
le payload du webhook).
bot_result prend l'une des trois valeurs canoniques :
| Résultat | Description |
|---|---|
human | Aucun indicateur d'automatisation trouvé. Les crawlers vérifiés des moteurs de recherche se résolvent aussi en human. |
bot | Accès automatisé ou scripté détecté. Une étiquette bot_type de forme libre l'accompagne. |
uncertain | Signaux mixtes ou faibles — ni clairement humain, ni clairement automatisé. |
Cela est mappé sur le champ métier decision (real, fake ou suspicious)
utilisé dans tout le tableau de bord, et sur bot.result dans le
payload du webhook.
"bot")bot_type est une chaîne de forme libre. Les valeurs ci-dessous sont des exemples illustratifs de
ce qu'émettent les détecteurs, et non un enum exhaustif :
| Type | Méthode de détection |
|---|---|
webdriver | navigator.webdriver vaut true |
selenium | Propriétés window/document spécifiques à Selenium détectées |
puppeteer | Indicateurs headless Puppeteer/Chromium correspondants |
playwright | Marqueurs spécifiques à Playwright ou chaîne d'UA détectés |
headless | 2+ des 11 marqueurs headless déclenchés |
phantomjs | Globales PhantomJS détectées (callPhantom, _phantom) |
nightmare | Globales NightmareJS détectées |
unknown | Anomalie d'eval length ou autres indicateurs d'automatisation non spécifiques |
rateBot | L'analyse comportementale de débit a dépassé les seuils |
La détection la plus directe. Tous les principaux frameworks d'automatisation mettent navigator.webdriver = true conformément à la spécification WebDriver. Cela est vérifié à la fois dans le frame principal (s300) et dans une iframe cachée (s158) pour attraper les outils qui ne patchent que le frame principal.
Analyse exhaustive des propriétés window et document à la recherche d'artefacts spécifiques à chaque framework :
| Framework | Propriétés détectées |
|---|---|
| Selenium | _Selenium_IDE_Recorder, _selenium, calledSelenium, __selenium_evaluate, $cdc_* (ChromeDriver) |
| Puppeteer | domAutomation, domAutomationController, UA HeadlessChrome |
| Playwright | __playwright, flag automation-controlled |
| PhantomJS | callPhantom, _phantom |
| NightmareJS | __nightmare, nightmare |
| CefSharp | CefSharp |
| Awesomium | awesomium |
| WebDriverIO | wdioElectron |
| Cypress | Propriété window __cypress |
L'analyse utilise également un motif regex /^([a-z]){3}_.*_(Array|Promise|Symbol)$/ pour détecter les globales auxiliaires injectées par Selenium.
11 marqueurs sont évalués, avec un seuil de 2 ou plus pour la classification :
| Marqueur | Signal | Condition |
|---|---|---|
| webdriver | s300 | = "true" |
| variantes de vendor manquantes | s28 | Aucun objet chrome/safari sur window |
| CDP headless chrome | s305 | headlessChrome = true |
| notification refusée | s88 | Notification.permission = "denied" à la première visite |
| chrome sans runtime | s89 | window.chrome existe mais chrome.runtime manque |
| langue unique | s90 | navigator.languages.length <= 1 |
| écran par défaut 800x600 | s5 | Résolution par défaut de Chrome headless |
| UA HeadlessChrome | UA | Le User-Agent contient "HeadlessChrome" |
| dimensions extérieures nulles | s97 | outerWidth = 0 et outerHeight = 0 |
| WebGL SwiftShader | s70 | Le renderer contient "swiftshader" (GPU logiciel) |
| WebGL llvmpipe | s70 | Le renderer contient "llvmpipe" (GPU logiciel) |
Les bons bots connus sont reconnus par leur motif de User-Agent. Chacun est vérifié par DNS inverse pour empêcher l'usurpation :
.googlebot.com)| Bot | Domaines autorisés |
|---|---|
.googlebot.com, .google.com | |
| Bing | .search.msn.com |
| Apple | .applebot.apple.com |
| Yahoo | .crawl.yahoo.net |
| Yandex | .yandex.com, .yandex.net, .yandex.ru |
| DuckDuckGo | .duckduckgo.com |
Si une requête prétend être Googlebot mais que le DNS inverse de son IP se résout vers un domaine étranger (une incohérence PTR confirmée, et non simplement une recherche absente ou expirée), elle est classée comme bot (type spoofed:google).
Détection basée sur le débit avec scoring d'entropie :
| Débit (req/min) | Seuil d'entropie | Action |
|---|---|---|
| 120+ | < 0.1 | bot (type : rateBot) |
| 60-120 | < 0.3 | bot (type : rateBot) |
| 45-60 | < 0.1 | bot (type : rateBot) |
| < 45 | Quelconque | Normal |
Règle supplémentaire : l'absence d'événements souris/clavier après plus de 30 secondes d'ancienneté de session classe la visite comme bot avec le type noInteraction.
eval.toString().length produit des valeurs cohérentes selon le moteur :
Toute autre valeur indique que la fonction eval a été interceptée par un framework d'automatisation, ce qui classe la visite comme bot avec le type unknown.
Le SDK client expose un booléen de commodité, result.bot.detected :
const result = await tracio.getResult()
if (result.bot.detected) { // Log and block console.warn(`Bot detected (confidence ${result.bot.confidence})`) showCaptcha() return}
// Proceed with loginawait login(credentials)Sur votre serveur, agissez sur la livraison du webhook. La détection des bots
réside dans l'objet bot — bot.result et bot.type :
// `event` is the webhook delivery body (/docs/webhooks)app.post("/webhook/tracio", async (req, res) => { const event = req.body
if (event.bot.result === "bot") { await db.blockedRequests.insert({ visitorId: event.visitorId, botType: event.bot.type, ip: event.ip, timestamp: new Date(), }) }
res.status(200).send("OK")})La détection des bots de TRACIO est conçue pour des faux positifs quasi nuls. Toutefois, des cas limites peuvent survenir :
| Scénario | Risque | Atténuation |
|---|---|---|
Extensions de navigateur modifiant navigator | Faible | La validation croisée de plusieurs signaux empêche les déclenchements sur un signal unique |
| Logiciels de sécurité d'entreprise | Très faible | La détection headless exige 2+ marqueurs |
| Outils d'accessibilité | Nul | Les API d'accessibilité ne déclenchent aucun signal de détection |
| Utilisateurs de VPN/proxy | Nul | L'usage de VPN est suivi séparément de la détection des bots |
Si vous rencontrez des faux positifs, examinez le champ bot.type pour comprendre quel détecteur a été déclenché et ajustez votre politique en conséquence.