Dans un article précédent, nous avons fait une affirmation. Quelle que soit la façon dont un script dessine le curseur, qu'il le téléporte droit sur la cible, suive une courbe de bézier ou fasse tourner une bibliothèque de physique comme WindMouse ou NaturalMouse, un modèle de mouvement l'attrape quand même. La forme d'un mouvement n'est que la moitié de ce qu'une main laisse derrière elle. Nous avons montré que la même chose vaut pour Playwright et l'API "humanlike" de browserless.io.
Cela soulève la question évidente de l'autre côté de la table. Et si vous arrêtiez de dessiner la forme à la main pour apprendre le reste ? Nous avons construit l'outil qui le fait. Il s'appelle human_nav : un outil de red team de recherche qui synthétise un mouvement humain de curseur, de défilement et de frappe pour éprouver la détection comportementale des bots. Voici ce qu'il a révélé sur les sites qui s'appuient le plus sur la biométrie comportementale.
La version courte :
- Les humaniseurs standards (courbes de bézier, WindMouse, NaturalMouse) sont attrapés de 97% à 100% du temps par un modèle de mouvement de bureau. La géométrie n'a jamais été la réponse.
- human_nav jette la géométrie réglée à la main. Trois petites politiques d'apprentissage par renforcement génèrent des trajectoires de curseur, des fenêtres de défilement et des rythmes de frappe, entraînées sur du mouvement humain réel : cadence, retours en arrière, la pause entre les mots.
- Face aux détecteurs figés sur lesquels il a été entraîné, il atteint la bande humaine. Face à un détecteur en direct qui a dérivé, l'écart se rouvre. La séparabilité du défilement tourne autour d'un AUC de 0,77, la politique de curseur peut encore passer au-dessus d'un seuil en direct, et la cadence de frappe peut sortir de la plage humaine.
- Le résultat penche du côté des défenseurs. La forme est un problème résolu pour l'attaquant. Ce qui sépare encore une politique d'une personne, c'est la stabilité de la cadence, la cohérence entre canaux et un détecteur qui continue de bouger.
Pourquoi ce sont les cibles difficiles
Les défenses anti-bots qui survivent à un opérateur compétent ne sont pas celles qui vérifient si le navigateur est headless. Cette bataille a été gagnée et perdue il y a des années. Les défenses qui mordent encore observent comment la session se comporte : le micro-timing d'un défilement, l'hésitation d'un curseur, le rythme de la frappe dans une barre de recherche. Les grandes marketplaces, les réseaux professionnels et les fils sociaux à forte rotation, les Amazon, LinkedIn et Reddit du web, s'appuient sur cette couche, parce que c'est celle qu'un navigateur furtif et un fingerprint propre ne franchissent pas gratuitement.
Cela en fait la bonne cible pour un outil de red team comportemental. Pas pour les casser, mais pour découvrir quelle part de cette couche comportementale est de la vraie sécurité et quelle part est un ralentisseur qui plie dès que le mouvement vient de quelque chose de mieux qu'une courbe de bézier. Alors nous avons construit ce quelque chose de mieux.
Comment fonctionne human_nav
L'idée est étroite. Ne scriptez pas le mouvement. Échantillonnez-le depuis une politique qui a appris à quoi ressemble le mouvement humain. Trois modèles distincts, chacun une petite politique d'apprentissage par renforcement tournant localement, chacun propriétaire d'un canal.
Chaque déplacement, défilement et frappe est routé à travers un serveur de politiques local avant de toucher la page. L'automatisation demande "aller en B" ou "taper ceci", et la politique renvoie le mouvement exact, point par point, à rejouer.
| Canal | Observation → action | Émet | Paramètres |
|---|---|---|---|
| Curseur | 19 → 3 | trajectoire A→B de pas (dx, dy, dt ms) | ~40k |
| Défilement | 13 → 2 | fenêtre de molette de ticks (dy, dt) | ~9k |
| Frappe | 33 → 2 | timing (hold, flight) par touche | ~35k |
Chacune est un MLP acteur-critique compact entraîné avec PPO : PyTorch pur, CPU, une politique gaussienne à écrasement tanh, une normalisation des observations en continu, un bonus d'entropie, un arrêt anticipé sur KL cible. Rien de tout cela n'est exotique. L'optimiseur n'a jamais été le sujet. La récompense, si.
Voici ce qui la distingue de tout humaniseur standard. Une bibliothèque de bézier optimise l'apparence d'une trajectoire. Ces politiques sont notées par des copies figées des propres détecteurs de cside, et récompensées dans l'espace des logits pour produire un mouvement que ces détecteurs lisent comme humain. Elles ne dessinent pas une plus jolie courbe. Elles résolvent ce dont la courbe n'a jamais été qu'un substitut.
À l'intérieur de la politique de curseur
L'agent de curseur voit une observation à 19 dimensions : le vecteur vers la cible, son dernier (dx, dy, dt), l'index de pas, la longueur cumulée de la trajectoire, le déplacement net, des statistiques courantes de vitesse et de timing, un compte de changements de direction, un ratio de rectitude et une anticipation du prochain waypoint. Cet état chargé d'accumulateurs est délibéré. Les détecteurs lisent la cinématique agrégée de toute la trajectoire, donc la politique reçoit des statistiques suffisantes en continu pour exactement les agrégats sur lesquels elle est jugée. Elle agit en émettant un triplet (dx, dy, dt) à chaque pas, borné à ±40 px et 4 à 40 ms.
La récompense est une porte. Un bonus d'arrivée de 20 domine tout, donc la politique apprend d'abord à réellement arriver en B. Ce n'est qu'à l'arrivée qu'elle collecte une récompense du détecteur, fixée à la marge humaine du critique contraignant, le pire de deux évaluateurs figés : cursor_v2 (une tête MLP, seuil autour de 0,992) et cursor_v1 (une tête LightGBM, seuil autour de 0,828). Passez les deux sous le seuil à la fois et elle gagne un bonus de réalisme par-dessus. L'entraînement suit un curriculum : navigation pure d'abord, puis pression croissante des détecteurs contre les deux critiques ensemble. Combattre deux détecteurs à la fois est ce qui garde les trajectoires propres et doucement courbées, au lieu de s'effondrer en un artefact qui trompe un évaluateur et paraît cassé à l'autre.
Six vraies trajectoires A→B sorties directement de la politique, déterministes, montrent le curseur accélérant au milieu et s'adoucissant à l'entrée de la cible, le ralentissement à l'approche qu'une main fait plutôt qu'un glissement à vitesse constante. La rectitude médiane entre elles est d'environ 0,99, sur 12 à 26 points, à environ 38 ms par pas. Le timing par pas reste presque plat dans les trente-et-quelques millisecondes. La politique a trouvé que le micro-timing stable, pas la gigue, est ce que les détecteurs figés lisent comme humain.
Cette solution est une aiguille aiguisée et déterministe. L'action moyenne de la politique est ce qui tombe dans la bande humaine. Échantillonnez-la de façon stochastique, ou ajoutez votre propre bruit par-dessus, et le réalisme s'effondre. Le hasard est exactement ce vers quoi l'automatisation naïve se tourne, et ici c'est le signe révélateur. La victoire est aussi étroite. C'est un exploit adversarial d'un détecteur figé, pas un mouvement humain certifié.
Défilement et frappe
La politique de défilement émet (dy, log1p(dt)) par tick de molette. Le timing est généré en espace logarithmique puis remis à l'échelle, donc une seule politique couvre tout, des rafales de moins de 10 ms aux pauses de stabilisation d'une seconde. Elle échantillonne ses tâches de défilement, la longueur, la distance nette et les retours en arrière, depuis une banque de défilements humains réels. Chaque fenêtre générée entre et sort en douceur au lieu de monter en ligne droite, et les inversions de direction tombent sur des écarts plus longs, une personne qui marque une pause avant de corriger.
La politique de frappe tourne sur une observation à 33 dimensions dont la queue est un one-hot des catégories de la touche actuelle et des deux suivantes (lettre, chiffre, espace, édition), et émet une paire (hold, flight) par touche. Les temps de hold restent près de 150 ms, mais les écarts de flight portent le signal.
| Timing de frappe | Valeur |
|---|---|
| Temps de hold | ~150 ms |
| Flight intra-mot | 250 à 350 ms |
| Flight en limite de mot | ~850 ms |
La politique a appris à marquer une pause entre les mots, un rythme qu'un délai fixe entre touches ne produit jamais. C'est toute l'idée. Remplacez la géométrie écrite à la main par une politique apprise par canal, notez-la contre un vrai détecteur, et laissez-la trouver les parties du mouvement humain qu'une personne écrivant mouse.move() ne penserait jamais à encoder : le ralentissement à l'approche, la pause avant une correction, le temps entre les mots.
Où atterrissent ces trajectoires
Une façon de voir ce que la politique nous a apporté : prenez un tas de trajectoires générées, des humaniseurs naïfs et de human_nav, réduisez chacune à dix caractéristiques cinématiques, et projetez le tout en trois dimensions par PCA. Les familles de droite bruitée et de bézier se rangent dans leurs propres amas serrés, parce qu'il n'y a qu'un nombre limité de façons de dessiner une courbe d'apparence lisse. La politique s'étale sur une région plus large et plus désordonnée, plus proche de la façon dont le vrai mouvement d'une main se disperse, ce qui est la propriété qu'une courbe fixe ne peut pas produire.
Lisez cela honnêtement. La projection couvre 440 trajectoires issues de quatre générateurs synthétiques, avec 77% de la variance tenue dans trois axes. Elle compare des générateurs synthétiques entre eux, pas contre des captures humaines réelles. Le constat est donc "la politique occupe une région différente et plus large que les humaniseurs standards", pas "la politique est indistinguable d'un humain".
À quel point ça s'approche vraiment
C'est ici que l'honnêteté compte plus que le titre. Face aux détecteurs figés sur lesquels les politiques ont été entraînées, elles gagnent. C'est ce que "entraîné contre eux" veut dire, et en soi cela ne prouve presque rien. Le vrai test, c'est un détecteur que la politique n'a jamais vu, et idéalement un qui a dérivé depuis. Là, le tableau est mitigé, et le mélange est le constat.
| Canal | Face au détecteur figé | Face à un détecteur en direct qui a dérivé |
|---|---|---|
| Curseur | Dans la bande humaine | Peut passer au-dessus d'un seuil en direct. Pas humain certifié. |
| Défilement | Dans la bande humaine | Séparable autour d'un AUC de 0,77. Réaliste, pas invisible. |
| Frappe | Dans la bande humaine | Le taux de pause et la vitesse peuvent sortir de la plage humaine. |
Lisez ce tableau comme un défenseur devrait le faire. Une politique apprise est un grand pas en avant par rapport à une courbe de bézier. Elle comble l'essentiel de l'écart de géométrie que les humaniseurs standards ne touchent jamais. Mais "plausiblement humain face au détecteur sur lequel je me suis entraîné" n'est pas "humain". Dès que le détecteur d'en face en est un qu'elle n'a jamais étudié, ou un qui a bougé depuis l'entraînement, le signal résiduel revient. Le réalisme du mouvement décline sous la dérive, et la dérive est la seule chose qu'un défenseur contrôle entièrement.
Ce que cela signifie si vous exploitez de la détection comportementale
Quelques conséquences en découlent, et ce sont la raison pour laquelle nous construisons des outils de red team.
D'abord, ne déployez pas une vérification comportementale pour la figer. Toute politique entraînée contre un détecteur statique finira par l'égaler. La défense la plus efficace de ce tableau, c'est la colonne de droite : recapturer et réentraîner selon un calendrier. Ce n'est pas de l'entretien, c'est le vrai mécanisme. Un détecteur qui bouge plus vite qu'un attaquant ne peut réentraîner en est un sur lequel il ne converge jamais.
Ensuite, notez la cohérence, pas les canaux isolément. Une politique qui maîtrise le mouvement de curseur et une politique qui maîtrise la frappe restent deux échantillonneurs distincts. Les corrélations qu'une vraie personne produit entre bouger, défiler et taper sont bien plus difficiles à falsifier que n'importe quel canal seul, parce que personne n'a entraîné de politique sur la distribution conjointe. Cette couture est là où un ensemble de bonnes contrefaçons mono-canal se défait.
Enfin, gardez le comportemental comme une couche, pas la couche. Le mouvement comportemental est puissant, et c'est aussi la couche la plus exposée à un humaniseur déterminé. Associez-le à des signaux de fingerprint, de réseau et de TLS, à la façon dont la pile de cside décide is_bot comme un appel combiné plutôt que de se fier à un seul modèle, et un opérateur doit battre toutes les couches à la fois, pas seulement celle dans laquelle il a englouti une politique apprise. C'est l'argument en faveur d'une cascade, et c'est pourquoi la détection de bots tient quand une seule vérification plierait.
Divulgation responsable. Ce compte rendu couvre la technique et ses limites au niveau des résultats. Il laisse de côté les internes du détecteur, les seuils, les définitions de caractéristiques par canal et toute procédure de réglage, la même ligne que tiennent nos autres articles publics. L'outil n'est pas distribué. Tout ce qui touche ici à une plateforme externe a été partagé avec cette plateforme avant publication.
L'intérêt de construire l'attaquant
Il est facile de lire human_nav comme un outil d'évasion qui se trouve vivre dans une boutique de détection. Nous l'avons construit pour la raison inverse. La seule façon de savoir si une défense comportementale est de la vraie sécurité ou un ralentisseur, c'est de construire le meilleur attaquant possible et de mesurer exactement où il cesse de fonctionner. La réponse ici est utile dans les deux cas. Le mouvement appris bat la géométrie, et il ne bat toujours pas un détecteur qui continue de bouger et lit plus d'un canal à la fois. C'est un résultat inconfortable si vous vendez de l'automatisation "humanlike", et rassurant si votre travail est de tenir les bots à l'écart.
cside vous montre chaque script et chaque session qui touche votre site, y compris l'automatisation qui a appris à bouger comme une main. Voyez ce qui tourne vraiment dans les navigateurs de vos utilisateurs.





