BotGuard et SearchGuard, octobre 2026
Étude publiée en janvier 2026, mise à jour le 19 octobre 2026.
Le corpus d'exécution d'origine présenté ci-dessous date du 17 octobre. Les expériences du 18 octobre sont résumées à la fin de ce rapport et dans le document de l'étude « Expériences du 18 octobre 2026 » ; les dénominateurs ne doivent pas être regroupés.
La protection de Google côté client évolue alors que BotGuard conserve la version majeure 41. En mars, les moteurs intègrent du code pour les iframes et le chargement de scripts. En août, le programme encodé du contrôle de fond grossit d'environ 13 %. En septembre, le canal de fond est associé à Google Flights.
Évolution de la protection
Les dates renvoient aux mécanismes établis par cette étude, pas nécessairement à leur premier déploiement ni à une généralisation à tous les utilisateurs.
| Période | Mécanisme |
|---|---|
| Octobre 2024 | BotGuard utilise déjà la version 41 |
| Janvier 2025 | Le code des résultats traite sg_ss et prévoit un repli sans JavaScript |
| Mai 2025 | Page intermédiaire knitsail, cookie SG_SS et relance de la navigation |
| Octobre 2025 | /sgasync livre un script, son hash et son programme |
| Novembre 2025 | Canal QyE de contrôle de fond |
| Janvier 2026 | Contrôle ON4 déclenché par une requête, puis nouvelle tentative |
| Mars 2026 | Les moteurs intègrent du code pour les iframes et le chargement de scripts |
| Juillet 2026 | Une partie des réponses du contrôle de fond ajoute un module qui enregistre des données dans localStorage et les relit au passage suivant |
| Août 2026 | Quatre nouvelles familles de tests dans le programme de fond (réglages déclarés par le navigateur, rendu du texte, précision du pointeur, WebGPU) ; le programme encodé grossit d'environ 13 % |
| Septembre 2026 | Canal de fond associé à Google Flights |
| Octobre 2026 | Test WebGPU retiré du programme de fond le 5 octobre |
Rôles à l'exécution
| VM | Présence dans neuf pages de résultats | Rôle observé |
|---|---|---|
| Résultats | 9/9 | Listeners de clic et tactiles, visibilité et mesures de temps ; aucune lecture de navigator, screen ou canvas observée |
| Knitsail | 5/9 | Inspection du navigateur et de l'environnement, signaux de souris, de clavier et de molette ; envois Validate |
| Publicité | 9/9 | Attestation publicitaire, y compris des requêtes d'images ; une destination de token distincte |
| YouTube | 2/9 | Attestation dans un lecteur intégré ; code et version non conservés |
Les quatre VM ne sont pas présentes sur toutes les pages. Les résultats classiques et le Mode IA figurent tous deux parmi les pages avec knitsail. Aucun listener scroll n'a été observé dans les sessions tracées. Le script de la page d'accueil a été analysé statiquement ; disposer de son code ne fournit pas de trace d'exécution de ses lectures du navigateur.
La page intermédiaire initiale reçoit HTTP 200, produit SG_SS et relance la recherche. L'intervalle observé entre la première requête de recherche et la nouvelle tentative va de 63 à 262 ms. Un parcours qui échoue peut mener, par une redirection, à /sorry, à un HTTP 429 et à reCAPTCHA. Un statut HTTP seul ne permet pas de savoir si une réponse de recherche contient des résultats ou une page intermédiaire.
Enveloppe /sgasync
Le client ajoute 97 à chaque octet de la réponse, modulo 256, puis décode l'UTF-8 et analyse le résultat. Cette transformation de transport réversible est distincte du chiffrement du token.
Les réponses examinées comptent huit positions, numérotées à partir de zéro :
| Position | Contenu |
|---|---|
| 0 | Marqueur bfkj |
| 1 | Tableau de six éléments, avec cinq null et le JavaScript à l'indice 5 |
| 2 | null |
| 3 | SHA-256 en Base64url du JavaScript embarqué |
| 4 | Programme encodé, préfixe de la fonction d'entrée compris |
| 5 | Espace de noms knitsail |
| 6 | null |
| 7 | Chaîne auxiliaire dont le sens n'est pas établi |
Les hash des scripts embarqués examinés correspondent au champ 3. Le moteur et le programme sont des artefacts distincts. Les préfixes de fonction d'entrée correspondent aux trois premiers caractères du programme dans les 26 programmes capturés pendant les sessions d'exécution.
En-têtes et codes de branche locaux
x-sg-e annonce un mode de traitement de la requête et les capacités du client. La valeur capturée initialement est 2,isrpe=1,hcs=1 ; le pilote VPS capture aussi 1,isrpe=1,hcs=1. Le client conserve le mode 1 lorsqu'il est demandé et utilise 2 sinon. isrpe provient de isrpeEnabled ou de window.sglc.isrpe ; hcs provient de hasCaptchaSupport ou de window.sglc.hcs.
X-Sg-Cs est lu dans les réponses. Un gestionnaire distingue les préfixes s et a ; un autre teste les valeurs exactes s et a. Le préfixe sfl reçoit un traitement particulier. X-Sg-Ss transporte un token calculé lors d'une requête relancée et se distingue du cookie SG_SS. X-Sorry-Redirect, ou une URL de réponse /sorry/index, intervient dans le parcours CAPTCHA.
Dans la fonction de nouvelle tentative examinée, les codes locaux signifient :
- 0 : calculer un token et réessayer avec
X-Sg-Ss. - 1 : émettre
sglacte. - 2 : émettre
sglhcteet attendresglhcte_retryavant de réessayer. - 3 : renvoyer la réponse sans passer par ces branches.
Ce sont des codes de branche locaux, pas des scores de confiance. Des surcouches installées tôt ou de façon différée autour des requêtes fetch/XHR de même origine communiquent par __sg_lite_queue.
Message Validate
Les neuf appels du corpus initial du 17 octobre sont des requêtes GET qui portent un tableau JSON dans le paramètre de requête reqpld. Leurs réponses valent [""] ; cela n'établit ni acceptation ni rejet.
| Champ du message | Contenu observé |
|---|---|
| 1, indice 0 du tableau | Token texte, 1 471 à 1 544 caractères |
| 2, indice 1 du tableau | Contexte ; le champ imbriqué 9 contient un sous-message avec ei au champ 1 |
| 3, indice 2 du tableau | Null dans les captures ; le code prévoit une alternative binaire dans le même groupe exclusif que le champ 1 |
| 4, indice 3 du tableau | 3 dans toutes les captures ; un autre chemin du code accepte 1 et 2 |
Commutateurs de configuration
Le client analysé tire sa configuration de champs de bits. Ses noms de propriétés locaux, obfusqués, ne sont pas des noms d'API stables :
Obconditionne l'initialisation ;Qbchoisit entre un démarrage synchrone et une attente d'une seconde suivie d'un rappel en période d'inactivité.Ddpeut déclencher la production du résultat lorsque la page devient masquée.Bdsaute la récupération du résultat de la VM dans sa branche.Cdconditionne l'envoi ;vcchoisit/sgvalidau lieu de Validate.bdetcdpermettent un envoi différé, par un rappel sur DOMContentLoaded ou par un délai supplémentaire d'une seconde.Lcetzdpilotent des chemins de télémétrie distincts pour les étapes et pour les durées.
La présence du code ne prouve pas qu'un commutateur était activé dans chaque session. Le chemin de requête synchrone sélectionne ON4, l'autre branche de chargement BMW ; le trafic de fond capturé utilise QyE.
Moteur et statistiques
Les moteurs examinés déclarent la version 41 ; le nombre de tours extrait est de 14, 15 ou 16. Les constantes et les bornes de boucle peuvent être des arguments plutôt que des littéraux. L'extracteur suit les arguments littéraux des appels et gère les deux sens de comparaison ; lire seulement les nombres voisins donne des résultats erronés.
| Copie locale | Tours | Constante |
|---|---|---|
| Résultats, 17 octobre | 15 | 2905 |
| Publicité, 17 octobre | 15 | 2905 |
| Knitsail, 17 octobre | 16 | 1088 |
| Page d'accueil, 18 octobre | 15 | 1795 |
Le chiffrement combine addition, rotation et XOR. Sa structure seule ne permet pas de l'identifier à un chiffrement répertorié. La rotation des hash de scripts et des constantes ne révèle pas la fenêtre pendant laquelle le serveur accepte des tokens plus anciens.
La routine statistique calcule l'écart-type de population, sqrt(M2/n), par mises à jour incrémentales. Un réservoir borné existe aussi. Ces routines n'établissent pas de seuils de détection côté serveur. Des indices de registre allant jusqu'à 511 sont compatibles avec un espace de 512 emplacements ; on ne peut pas supposer que le rôle de chaque registre reste stable d'une variante de script à l'autre.
Limites
L'étude distingue les capacités du code en analyse statique, l'exécution observée et les décisions du serveur. Knitsail et YouTube inspectent l'intégrité des accesseurs et peuvent détecter une instrumentation. Aucune vérification d'accesseur n'a été observée pour les VM des résultats et de la publicité, mais cela ne garantit pas que l'instrumentation soit restée sans effet. Les règles de décision du serveur, les scores de risque, les fenêtres d'acceptation et l'efficacité de la détection ne sont pas révélés par cette analyse.
La page publiée contient les tableaux d'observation détaillés et la chronologie.
BotGuard / SearchGuard : expériences du 18 octobre 2026
Corpus distincts
- 17 octobre : quatre sessions, trois profils, neuf SERP, 43 documents Google. Les observations détaillées des lectures des VM appartiennent à ce corpus.
- Pilote VPS : 37 essais enregistrés, plus une navigation supplémentaire dans une session ouverte. Trois enregistrements se sont arrêtés au contrôle d'IP, avant Google. Comparaison avec Chromium invalidée par des problèmes de version et de contrôles.
- Série contrôlée sur VPS : 21 navigations, trois profils sources neufs et neuf paires NID conservé/supprimé, une sortie VPN américaine fixe.
- Travail hors ligne : 96 replays de VM sur quatre échantillons (64 de la matrice initiale + 16 contrôles du tirage aléatoire + 16 contrôles conservant l'enveloppe), plus 48 cas synthétiques du contrôleur.
Accès et session
Sur le VPN suisse initial, Camoufox avec profil neuf a obtenu trois SERP après challenge ; Chrome avec interface et Chrome headless ont chacun enregistré trois refus. Le client HTTP sans JavaScript est resté sur un challenge dans trois essais. Sur la sortie native du VPS, Camoufox avec profil neuf a obtenu trois SERP après challenge, Chrome avec interface trois refus. Ce sont des observations par configuration, pas des taux de réussite généraux.
Série contrôlée : les trois sources neuves ont obtenu des SERP après challenge ; les neuf copies avec NID conservé ont obtenu des SERP directes ; les neuf copies avec NID supprimé ont obtenu des SERP après challenge. L'IP était stable avant et après chaque navigation ; l'identité du navigateur observée à l'exécution était identique. Huit paires sur neuf ne différaient que par NID parmi les cookies réellement transmis ; une différait aussi par __Secure-STRP. Les attentes de 5/35/120 secondes partent de l'ouverture du contexte, pas de l'émission de SG_SS, et n'établissent pas la validité du cookie à 30 secondes.
Binaire Camoufox Firefox 135.0.1-beta.24 ; paquet Python 0.4.11 ; Playwright 1.51.0. Chrome 153.0.8010.52. Le Chromium 141 en cache (révision 1194) ne correspondait pas à la révision 1161 attendue, absente. Requête : musée du Louvre site officiel, fr-FR, hl=fr, gl=fr. La série contrôlée a utilisé une sortie américaine fixe alors que la France était demandée ; toutes les branches partagent cette condition.
La qualité n'est pas certifiée
Les 21 SERP contrôlées contiennent toutes un domaine louvre.fr identifié. Aucune vidéo n'a été détectée par les résultats H3, les liens ou cartes de sources vidéo identifiables ou les titres de modules ; cette heuristique peut manquer des cartes sans libellé. Trois paires sur neuf ont des ensembles de titres extraits disjoints. Le titre candidat de la page d'accueil apparaît au rang 1 sur trois pages, au rang 7 sur dix-sept, et n'est pas identifié sur une. La présentation des sitelinks influe aussi sur l'extraction.
Les six pages avec NID conservé où le candidat de l'accueil est au rang 7 enregistrent Validate, contrairement aux trois pages avec NID conservé où il est au rang 1, dans la fenêtre d'observation. C'est exploratoire : les pages passées par un challenge ont elles aussi l'accueil à un rang bas, sans Validate. Ce n'est pas un classifieur global de qualité.
Les dix-neuf SERP capturées du pilote montrent aussi des variations de classement sur VPN et sur IP native, sans vidéo détectée. Il n'existe aucune référence humaine indépendante contemporaine ; les liens /goto opaques n'ont pas été suivis. Ni un empoisonnement des résultats ni son absence ne sont établis. L'abondance de vidéos reste un signal candidat, pas une signature prouvée de détection. Les observations de la VM YouTube du 17 octobre appartiennent à un autre corpus.
Contrôles hors ligne des VM
Les sorties A/B d'origine sont stables sur les quatre échantillons. L'extraction brute modifie les quatre sorties. Rétablir un tirage Math.random manquant ne rétablit que la VM IoM extraite. Conserver l'enveloppe d'origine et le code interne extrait rétablit les quatre sorties ; reformater ce code interne modifie de nouveau les quatre, deux fois par condition. Cela va dans le sens d'une sensibilité au texte source, sans localiser un mécanisme d'intégrité précis. Des interceptions d'accesseurs supplémentaires laissent les sorties d'origine inchangées dans ce banc d'essai hors ligne ; cela ne prouve pas que l'instrumentation passe inaperçue en ligne. La couverture des cadres enfants about:blank immédiats reste incertaine. Aucun token généré n'a été envoyé à Google.
Les 48 cas synthétiques du contrôleur concordent avec les assertions et la reconstruction : totaux 4 092/4 093/4 094, ancien cookie présent/absent, écriture acceptée/refusée, ss_cgi true/false, ce=30/0. La condition de transport stricte < 4 093 et la vérification de présence d'un SG_SS périmé sont reproduites. Le repli à deux secondes est un chemin d'aide, pas nécessairement une annulation de la VM. L'acceptation par le serveur est inconnue.
Protocole capturé
Le pilote enregistre X-Sg-Cs:s puis, 678 ms plus tard, une nouvelle tentative sur /async/callback:6761 avec sei et X-Sg-Ss (1 072 caractères) en plus, suivie de X-Sg-Cs:a. La méthode, le corps et les autres paramètres comparés restent identiques ; la réponse passe de 59 à 260 350 octets. Mode de requête : 1,isrpe=1,hcs=1. Le HAR n'identifie pas la pile JavaScript à l'origine de la requête.
La série contrôlée enregistre douze documents intermédiaires avec ce=30, ss_cgi=false, challenge_version=0, r=1. Les longueurs de ses six tokens Validate sont 1 403, 1 404, 1 400, 1 424, 1 448 et 1 435 ; le pilote avait 1 441 et 1 417, contre 1 471 à 1 544 dans neuf appels du corpus initial. Aucun X-Sg-Cs n'apparaît dans la série contrôlée. Un HTTP 200, des réponses vides et des longueurs de token proches n'établissent pas l'acceptation.
Limites et éléments conservés
Le test de visibilité n'a pas été concluant : les onglets, la fenêtre pop-up et le retrait effectif de la fenêtre X11 de l'affichage n'ont jamais produit document.hidden=true. Aucune comparaison valide de Google entre page visible et page masquée n'est revendiquée. Les comparaisons d'instrumentation et de session du pilote comptaient une répétition par branche, avec des empreintes de navigateur non figées. Les services déjà présents sur le VPS sont restés actifs. Aucun compte Google personnel ; essais séquentiels, ordre alterné, pauses de 20 secondes ; un refus explicite met fin à l'essai, sans changer d'IP pour chercher un succès. L'accès natif du VPS a été testé avec parcimonie dans le pilote et n'a pas été repris en phase deux.
Les classes de parcours distinguent SERP directe, SERP après challenge, challenge non terminé, refus et autre. Les documents réseau et les navigations JavaScript complètent les vérifications de structure, de titres et de liens. Le hash des 201 fichiers de capture privés définitifs a été calculé et vérifié. La rotation du VPN a été rétablie et les minuteurs de reprise supprimés.