4 points par GN⁺ 2026-03-30 | Aucun commentaire pour le moment. | Partager sur WhatsApp
  • Lors de l’envoi d’un message dans ChatGPT, le programme Cloudflare Turnstile s’exécute et inspecte non seulement l’empreinte du navigateur, mais aussi l’état de l’application React
  • Le programme déchiffré collecte 55 propriétés et applique une procédure de vérification en trois couches : navigateur, réseau et application
  • La vérification ne peut être réussie que dans un véritable environnement SPA où le rendu React est terminé, ce qui signifie que les navigateurs headless et les simples requêtes de bot échouent
  • L’empreinte collectée est chiffrée puis convertie en OpenAI-Sentinel-Turnstile-Token, auquel s’ajoutent ensuite les modules Signal Orchestrator et Proof of Work
  • Comme seuls les serveurs de Cloudflare connaissent la clé de déchiffrement, la frontière de confidentialité est définie par la politique plutôt que par la technique

Analyse du fonctionnement de Cloudflare Turnstile lors de l’envoi d’un message dans ChatGPT

  • À chaque envoi de message dans ChatGPT, le programme Cloudflare Turnstile s’exécute automatiquement dans le navigateur
    • L’analyse de 377 programmes Turnstile déchiffrés à partir du trafic réseau montre qu’il ne se limite pas à une collecte classique d’empreinte navigateur, mais inspecte aussi l’état de l’application React
    • Les bots qui ne font que falsifier l’empreinte du navigateur ne passent pas la vérification ; il faut rendre complètement la SPA (application monopage) de ChatGPT pour la réussir

Structure de chiffrement et processus de déchiffrement

  • Le bytecode Turnstile est transmis dans le champ turnstile.dx de la réponse serveur et il est chiffré à chaque requête sous la forme d’une chaîne base64 de 28 000 caractères
    • La couche de chiffrement externe peut être déchiffrée par une opération XOR avec le jeton p, les deux valeurs étant échangées dans la même requête HTTP
    • Le résultat déchiffré est un bytecode JSON composé de 89 instructions de VM
  • À l’intérieur se trouve un blob chiffré supplémentaire de 19 KB, lui aussi chiffré avec une autre clé XOR
    • La clé est incluse dans le bytecode sous la forme d’une valeur littérale flottante (par ex. 97.35), générée par le serveur puis envoyée au navigateur
    • Sur 50 requêtes, un déchiffrement valide en JSON a été confirmé selon le même procédé
  • La procédure complète de déchiffrement se compose des 5 étapes suivantes
    1. Lire le jeton p dans la requête
    2. Lire turnstile.dx dans la réponse
    3. XOR(base64decode(dx), p) → génération du bytecode externe
    4. Extraire la dernière valeur comme clé depuis l’instruction à 5 arguments située après le blob de 19 KB
    5. XOR(base64decode(blob), str(key)) → déchiffrement du programme interne (417 à 580 instructions)

Éléments inspectés par le programme déchiffré

  • Le programme interne s’exécute sur une VM personnalisée avec 28 instructions (opcodes), et les adresses des registres flottants changent aléatoirement à chaque requête
  • Il collecte au total 55 propriétés, identiques dans les 377 échantillons
  • Couche 1 : empreinte du navigateur

    • 8 propriétés liées à WebGL : UNMASKED_VENDOR_WEBGL, UNMASKED_RENDERER_WEBGL, WEBGL_debug_renderer_info, etc.
    • 8 informations d’écran : colorDepth, pixelDepth, width, height, availWidth, availHeight, availLeft, availTop
    • 5 éléments matériels : hardwareConcurrency, deviceMemory, maxTouchPoints, platform, vendor
    • 4 mesures de police : création d’un div caché puis mesure de la taille de rendu via fontFamily, fontSize, getBoundingClientRect, innerText
    • 8 éléments d’exploration du DOM : createElement, appendChild, removeChild, style, position, visibility, ariaHidden, etc.
    • 5 éléments de stockage : storage, quota, estimate, setItem, usage
      • Les résultats sont stockés dans localStorage sous la clé 6f376b6560133c2c afin de persister entre les rechargements de page
  • Couche 2 : réseau Cloudflare

    • 5 en-têtes edge : cfIpCity, cfIpLatitude, cfIpLongitude, cfConnectingIp, userRegion
    • Ces valeurs n’existent qu’au sein du réseau Cloudflare ; les bots accédant directement au serveur d’origine obtiennent donc des champs absents ou incohérents
  • Couche 3 : état de l’application

    • 3 structures internes React : __reactRouterContext, loaderData, clientBootstrap
    • Ces éléments n’existent que lorsque l’application React de ChatGPT a été entièrement rendue et que l’hydratation SSR est terminée
    • Les navigateurs headless qui ne chargent que le HTML ou n’exécutent pas les bundles JS, ainsi que les frameworks de bots qui n’exécutent pas réellement React, échouent

Processus de génération du jeton

  • Après la collecte des 55 propriétés, le programme déchiffre un blob chiffré de 116 octets puis exécute 4 instructions finales
    • JSON.stringify(fingerprint)storeXOR(json, key)RESOLVE
    • La valeur obtenue est convertie en en-tête OpenAI-Sentinel-Turnstile-Token et incluse dans toutes les requêtes de conversation

Composants supplémentaires de Sentinel

  • En plus de Turnstile, deux modules de vérification supplémentaires sont présents
  • Signal Orchestrator

    • Composé de 271 instructions
    • Installe des écouteurs d’événements keydown, pointermove, click, scroll, paste, wheel
    • Suit 36 propriétés window.__oai_so_* afin de surveiller le timing de frappe, la vitesse de la souris, les schémas de défilement, le temps d’inactivité et les événements de collage
    • Il joue le rôle d’une couche biométrique comportementale en plus de la collecte d’empreinte
  • Proof of Work

    • Basé sur une empreinte de 25 champs + SHA-256 hashcash
    • La difficulté est un nombre aléatoire uniforme dans une plage de 400K à 500K ; 72 % sont résolus en 5 ms ou moins
    • Il inclut 7 indicateurs binaires de détection : ai, createPRNG, cache, solana, dump, InstallTrigger, data (tous à 0 dans 100 échantillons)
    • Il ajoute un coût de calcul, mais ne constitue pas le principal moyen de défense

Qui peut déchiffrer le jeton et implications de sécurité

  • Comme la clé XOR du programme interne est générée par le serveur puis intégrée au bytecode, seul le serveur qui a généré turnstile.dx connaît cette clé
  • La frontière de confidentialité entre l’utilisateur et l’opérateur du système est définie par une décision de politique, et non par une contrainte cryptographique
  • L’objectif de l’obfuscation est de
    • masquer les éléments collectés contre l’analyse statique
    • empêcher l’exploitant du site (OpenAI) de lire directement les valeurs brutes de l’empreinte
    • rendre chaque jeton unique afin d’éviter la réutilisation (replay)
    • rendre difficile la détection externe des changements opérés par Cloudflare sur les éléments inspectés
  • Cependant, le chiffrement repose sur une opération XOR avec la clé dans le même flux de données, ce qui n’est qu’une obfuscation destinée à compliquer l’analyse

Statistiques de collecte et d’analyse

Élément Valeur
Programmes déchiffrés 377/377 (100 %)
Utilisateurs uniques observés 32
Nombre de propriétés par programme 55 (identiques pour tous)
Nombre d’instructions 417–580 (moyenne 480)
Clés XOR (50 échantillons) 41
Propriétés de Signal Orchestrator 36
Champs de Proof of Work 25
Temps de résolution du PoW 72 % en 5 ms ou moins

Méthodologie d’analyse

  • Seul du trafic collecté selon une procédure légale a été utilisé
  • Aucune donnée personnelle d’utilisateurs n’a été publiée
  • Tout le trafic a été observé avec le consentement des participants
  • Le SDK Sentinel (sdk.js, 1 411 lignes) a fait l’objet d’une désobfuscation manuelle et d’un déchiffrement hors ligne
  • Le déchiffrement a été réalisé hors ligne en Python

Aucun commentaire pour le moment.

Aucun commentaire pour le moment.