[<< Sommaire](README.md) | [Suivant >>](03_architecture.md) --- ## 2. État de l'art ### 2.1 Détection par règles statiques #### 2.1.1 OWASP Core Rule Set (CRS) L'[OWASP Core Rule Set (CRS)](https://coreruleset.org/) est un ensemble de règles génériques de détection d'attaques pour les pare-feux applicatifs web (WAF — Web Application Firewall) compatibles ModSecurity et Coraza. La version 4.0, publiée en [février 2024](https://coreruleset.org/20240214/let-crs-4-be-your-valentine/), couvre les catégories d'attaques suivantes : **SQLi (SQL Injection)** : injection de code SQL malveillant dans des paramètres de requête ou formulaires pour manipuler les requêtes de base de données. Exemple : `' OR '1'='1` dans un champ de connexion pour contourner l'authentification. **XSS (Cross-Site Scripting)** : injection de code JavaScript dans des pages web dynamiques pour qu'il s'exécute dans le navigateur d'autres utilisateurs. Exemple : `` dans un commentaire. **LFI/RFI (Local File Inclusion / Remote File Inclusion)** : exploitation de fonctions d'inclusion de fichiers pour charger des fichiers arbitraires depuis le système local (LFI : `../../etc/passwd`) ou depuis un serveur distant (RFI : `http://attacker.com/shell.php`). **RCE (Remote Code Execution)** : exécution de commandes système arbitraires sur le serveur via une vulnérabilité applicative. Particulièrement destructeur car il donne à l'attaquant le contrôle complet de l'hôte. **Scanners connus** : signatures de Nikto, Nessus, OpenVAS, sqlmap, Nuclei — outils de reconnaissance et de fuzzing dont les User-Agents et patterns de requêtes sont documentés. **Points forts du CRS** : | Propriété | Valeur | |-----------|--------| | Déterminisme | Totalement déterministe, explicable | | Latence | < 1 ms par requête | | Couverture | OWASP Top 10, CWE courants | | Maintenance | Communauté active, mises à jour fréquentes | | Intégration | ModSecurity, Coraza, Nginx, Apache, F5 | **Limites fondamentales** : - **Absence de contexte session/comportemental** : chaque requête est évaluée de manière indépendante. Un scraper qui envoie 10 000 requêtes GET syntaxiquement correctes est totalement invisible pour le CRS. - **Vulnérabilité aux payloads polymorphiques augmentés par LLM** : [Osama et al., 2025 (arXiv:2512.23610)](https://arxiv.org/abs/2512.23610) démontrent que les WAF basés sur des règles (ModSecurity + CRS) n'ont qu'un taux de blocage de 10 à 14 % contre des payloads d'injection SQL et XSS générés et mutés par LLM, contre 96 à 100 % pour XGBoost entraîné sur des features comportementales. Les LLMs (GPT-4, Claude) peuvent paraphraser les payloads malveillants de manière à préserver leur sémantique d'injection tout en contournant les expressions régulières du CRS. - **Détection comportementale nulle** : un scraper syntaxiquement conforme, sans charge utile malveillante, est structurellement invisible pour le CRS. - **Coût de maintenance élevé** : les nouvelles techniques d'attaque nécessitent des mises à jour manuelles de règles, créant un délai systématique entre l'émergence d'une menace et sa couverture. #### 2.1.2 Listes de réputation IP et ASN La deuxième couche de défense statique repose sur des dictionnaires de réputation. Trois structures sont utilisées en production : **dict_bot_ip (IP_TRIE)** : dictionnaire de plages CIDR associées à des bots connus, implémenté comme un arbre radix (trie sur les bits de l'adresse IP) permettant une recherche par plus long préfixe en O(k) où k est la longueur de la clé (32 bits pour IPv4, 128 bits pour IPv6). L'opération de lookup par CIDR en O(1) amorti est possible grâce à l'indexation des nœuds internes du trie. **CIDR (Classless Inter-Domain Routing)** : notation pour spécifier des plages d'adresses IP, ex. `192.168.1.0/24` désigne les 256 adresses de `192.168.1.0` à `192.168.1.255`. Le suffixe `/24` indique que les 24 premiers bits sont le préfixe réseau. **ASN (Autonomous System Number)** : numéro unique identifiant un système autonome — un groupe de préfixes IP contrôlés par un seul opérateur réseau et partageant une politique de routage unifiée. Exemple : AS15169 = Google, AS32934 = Meta, AS14618 = Amazon AWS. Attribués par les RIR (Regional Internet Registry : ARIN, RIPE NCC, APNIC). Les ASNs sont la base de BGP (Border Gateway Protocol), le protocole de routage inter-domaines d'internet. **dict_bot_ja4** : mapping JA4 hash → bot_name pour les empreintes TLS connues associées à des outils spécifiques. **dict_asn_reputation** : mapping ASN → label parmi : `human` (FAI résidentiel), `datacenter` (AWS, GCP, Azure, OVH), `proxy` (Bright Data, Oxylabs), `tor` (nœuds de sortie Tor), `vpn` (ExpressVPN, NordVPN), `scanner` (Shodan, Censys, Masscan), `bot` (botnets connus). **Limite critique** : les botnets résidentiels opèrent depuis des ASNs étiquetés `human`. Les proxies résidentiels (Bright Data, Oxylabs) vendent un accès à des millions d'adresses IP de particuliers — souvent via des applications mobiles dont les conditions d'utilisation autorisent la réutilisation de la bande passante — pour contourner précisément les listes de réputation ASN. Une adresse IP Bright Data apparaît dans le même sous-réseau qu'un voisin résidentiel légitime, avec le même ASN de FAI. Ce vecteur de contournement est structurellement invisible pour les approches purement basées sur la réputation IP/ASN. #### 2.1.3 Projet Anubis (TecharoHQ) [Anubis](https://github.com/TecharoHQ/anubis) est un système de règles communautaire en YAML permettant de définir des actions granulaires par bot identifié. Les quatre actions disponibles sont : - **ALLOW** : autorisation explicite (bots légitimes : Googlebot, Bingbot, bots de recherche académique) - **DENY** : blocage avec retour 403 Forbidden — signal de vérité terrain fort pour l'entraînement HAT (River) - **WEIGH** : ajout d'un score de pondération sans blocage — signal auxiliaire dans le vecteur de features - **CHALLENGE** : redirection vers un challenge (PoW ou CAPTCHA) **Implémentation dans l'architecture** : deux structures de lookup sont maintenues en mémoire : - `dict_anubis_ip` : IP_TRIE CIDR pour les correspondances par plage IP - `dict_anubis_asn` : dictionnaire ASN → action/score **Priorité de correspondance** : `COALESCE(IP match, ASN match)` — une correspondance CIDR précise sur l'IP prend la priorité sur la correspondance ASN plus générale. Cela reflète le principe que l'information la plus spécifique est la plus fiable. **Valeur pour le pipeline ML** : - Les sessions `DENY` fournissent des étiquettes de bot à haute confiance pour l'entraînement incrémental du HAT (River), sans nécessiter d'annotation manuelle. - Les sessions `WEIGH` contribuent une feature binaire `anubis_is_flagged` dans la famille F7, enrichissant le vecteur de features sans déclencher de blocage. --- ### 2.2 Fingerprinting réseau #### 2.2.1 TLS Fingerprinting : de JA3 à JA4+ **Fondements : le message ClientHello TLS** Lorsqu'un client initie une connexion TLS (Transport Layer Security), il envoie un message `ClientHello` qui contient les informations suivantes : - **TLS versions supportées** : ex. TLS 1.2, TLS 1.3 - **Cipher suites** : paires algorithme de chiffrement symétrique + AEAD proposées par le client, ex. `TLS_AES_256_GCM_SHA384` (TLS 1.3), `TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256` (TLS 1.2). Le serveur choisit la suite la plus forte qu'il supporte. - **Extensions** : ensemble de champs optionnels étendant le protocole de base : SNI, ALPN, session ticket, supported_versions, key_share, signature_algorithms, etc. - **Elliptic curve groups (supported_groups)** : courbes elliptiques pour l'échange de clés ECDH, ex. x25519, secp256r1, secp384r1 - **Point formats** : formats de compression des points de courbe elliptique (généralement `uncompressed` uniquement en TLS 1.3) Ces champs sont déterminés par la pile TLS du client (OpenSSL, BoringSSL, NSS, SChannel) et varient peu d'une connexion à l'autre pour le même client, constituant une empreinte stable et distinctive. **JA3** [JA3 (Althouse et al., 2017, Salesforce)](https://github.com/salesforce/ja3) est un algorithme de fingerprinting TLS qui calcule le hash MD5 d'une chaîne de valeurs concaténées extraites du ClientHello : ``` JA3 = MD5(TLSVersion,Ciphers,Extensions,EllipticCurves,EllipticCurvePointFormats) ``` Exemple : `769,47-53-5-10-49172-49162-49157-49167-49161-100-56-19-49156-49162-49159-49160-22-3,0-10-11,23-24-25,0` JA3 a été largement adopté (Cloudflare, AWS, Akamai, Suricata, Zeek) mais présente deux problèmes fondamentaux : **Instabilité GREASE** : [RFC 8701](https://www.rfc-editor.org/rfc/rfc8701) (Generate Random Extensions And Sustain Extensibility) introduit un mécanisme par lequel les clients TLS injectent des valeurs « factices » dans leurs listes de cipher suites et d'extensions. Ces valeurs GREASE (0x0A0A, 0x1A1A, 0x2A2A, 0x3A3A, 0x4A4A, 0x5A5A, 0x6A6A, 0x7A7A, 0x8A8A, 0x9A9A, 0xAAAA, 0xBABA, 0xCACA, 0xDADA, 0xEAEA, 0xFAFA) sont choisies aléatoirement à chaque connexion. L'objectif est de s'assurer que les serveurs ne rejettent pas les extensions inconnues, évitant l'ossification du protocole. Conséquence : deux connexions successives du même navigateur Chrome peuvent produire des JA3 différents en raison du changement des valeurs GREASE, rendant l'empreinte non déterministe. **Collision inter-versions** : les mises à jour majeures de navigateur changent la liste des cipher suites supportés, modifiant le JA3 de manière légitime. Un JA3 associé à Chrome 110 est différent de celui de Chrome 120. **JA4** [JA4 (Althouse, FoxIO, septembre 2023)](https://github.com/FoxIO-LLC/ja4) est une refonte complète du fingerprinting TLS avec un format lisible par l'humain structuré en trois sections `a_b_c` : **Section a (lisible par l'humain)** : chaîne de 10 caractères encodant les métadonnées structurelles : - Type de protocole : `t` (TCP), `q` (QUIC), `d` (DTLS) - Version TLS négociée : `13` (TLS 1.3), `12` (TLS 1.2), `10` (TLS 1.0) - Nombre de cipher suites (2 chiffres, GREASE filtrés) - Nombre d'extensions (2 chiffres, GREASE filtrés) - Valeur ALPN de premier choix (`h2`, `h1`, `00` si absent) - Présence du SNI : `d` (domain, SNI présent), `i` (IP, SNI absent) **ALPN (Application-Layer Protocol Negotiation)** : extension TLS définie dans [RFC 7301](https://www.rfc-editor.org/rfc/rfc7301) permettant au client d'annoncer les protocoles applicatifs qu'il supporte dans le ClientHello (ex. `h2` pour HTTP/2, `http/1.1`, `h3`). Le serveur sélectionne un protocole dans la liste et le communique dans le ServerHello. ALPN permet le multiplexage de protocoles sur un même port TLS sans identification de port séparée. Son absence dans un ClientHello signale un client non-navigateur ou une bibliothèque TLS minimaliste. **SNI (Server Name Indication)** : extension TLS définie dans [RFC 6066](https://www.rfc-editor.org/rfc/rfc6066) qui envoie le nom d'hôte cible dans le ClientHello, permettant à un serveur d'héberger plusieurs certificats TLS sur une seule adresse IP (hébergement virtuel TLS). Sans SNI, un serveur multi-domaines ne peut pas savoir quel certificat présenter avant que la session TLS soit établie. **Section b** : SHA-256 tronqué aux 12 premiers caractères hexadécimaux de la liste de cipher suites triée (GREASE filtrés). Le tri élimine les variations non sémantiques dues à l'ordre aléatoire des cipher suites. **Section c** : SHA-256 tronqué aux 12 premiers caractères hexadécimaux de la liste d'extensions triée (GREASE filtrés, codes extensions seulement sans valeurs). Exemple de JA4 : `t13d1516h2_8daaf6152771_e5627efa2ab1` La combinaison du tri et du filtrage GREASE élimine la variabilité non sémantique, produisant une empreinte stable pour le même navigateur entre deux connexions. **Famille JA4+ (12 méthodes)** | Nom court | Nom complet | Signal capturé | Couche | |-----------|-------------|----------------|--------| | JA4 | JA4 TLS Client | Empreinte ClientHello TLS (version, ciphers, extensions, ALPN, SNI) | L5 TLS | | JA4S | JA4 Server | Réponse TLS serveur (version négociée, cipher choisi, extensions) | L5 TLS | | JA4H | JA4 HTTP | Ordre et présence des en-têtes HTTP, Accept-Language, cookies | L7 HTTP | | JA4L | JA4 Latency | Latence client→serveur (mesure de la distance physique) | L4 TCP | | JA4LS | JA4 Latency Server | Latence serveur→client | L4 TCP | | JA4X | JA4 X509 | Certificat TLS : algorithme de signature, extensions, OID | L5 TLS/X.509 | | JA4SSH | JA4 SSH | Échange SSH : kex algorithms, host key, encryption, MAC | L5 SSH | | JA4T | JA4 TCP | Paramètres SYN : Window Size, Options TCP, Window Scale, MSS | L4 TCP | | JA4TS | JA4 TCP Server | Réponse SYN-ACK serveur | L4 TCP | | JA4TScan | JA4 TCP Scan | Fingerprint de scanner TCP actif | L4 TCP | | JA4+ (composite) | Multi-protocol | Combinaison JA4+JA4H+JA4T pour cohérence cross-layer | L4–L7 | | (en développement) | | | | **Adoption en production (2026)** : [Cloudflare](https://developers.cloudflare.com/bots/concepts/ja3-ja4-fingerprint/), [AWS CloudFront](https://aws.amazon.com/about-aws/whats-new/2024/10/amazon-cloudfront-ja4-fingerprinting/), [AWS WAF](https://aws.amazon.com/about-aws/whats-new/2025/03/aws-waf-ja4-fingerprinting-aggregation-ja3-ja4-fingerprints-rate-based-rules/), Google Cloud Armor, Azure Front Door, Akamai, F5 BIG-IP, Suricata, Zeek, Arkime, VirusTotal, Fastly, Vercel. #### 2.2.2 TCP Fingerprinting **JA4T** capture les paramètres du paquet SYN TCP pour identifier le système d'exploitation et la configuration réseau du client. **Paquet SYN TCP** : premier paquet de la poignée de main à trois voies TCP (SYN → SYN-ACK → ACK). Il contient dans son en-tête : - **MSS (Maximum Segment Size)** : taille maximale du payload TCP en octets qu'un segment peut transporter. Pour Ethernet standard (MTU 1500), MSS = 1500 - 20 (IP header) - 20 (TCP header) = 1460 octets. Un MSS réduit (ex. 1380) signale la présence d'un tunnel VPN qui consomme une partie de l'espace disponible pour son propre en-tête d'encapsulation. - **Window Size** : taille du tampon de réception en octets, indiquant combien de données le récepteur peut accepter sans acquittement. Déterminé par l'implémentation de la pile TCP dans le noyau OS. - **TCP Options** : champ extensible contenant des options négociées. Les plus importantes : type 1 (NOP padding), type 2 (MSS), type 3 (Window Scale), type 4 (SACK Permitted), type 8 (Timestamps). - **Window Scale (RFC 7323)** : option TCP définie dans [RFC 7323](https://www.rfc-editor.org/rfc/rfc7323) permettant de dépasser la limite de 65 535 octets du champ Window Size (16 bits). Le facteur d'échelle est un exposant binaire (0–14) : `taille_effective = window_size × 2^scale`. Permet des fenêtres TCP jusqu'à 1 Go, indispensable pour les connexions à haute bande passante et forte latence (liaisons satellite, intercontinentales). - **TTL IP (Time to Live)** : compteur décrémenté par chaque routeur traversé. Valeurs initiales typiques : Linux=64, Windows=128, iOS/macOS=64. Après K sauts, TTL_observé = TTL_initial - K, permettant d'inférer TTL_initial et donc l'OS source. **Signatures OS caractéristiques** : | OS | Window Size | Options TCP | Window Scale | MSS | TTL initial | |----|------------|-------------|--------------|-----|-------------| | Linux 5.x+ | 64240 | 2-4-8-1-3 | 7 | 1460 | 64 | | Windows 11 | 64240 | 2-4-8-1-3 | 8 | 1460 | 128 | | macOS 14+ | 65535 | 2-4-8-1-3 | 6 | 1460 | 64 | | iOS 17+ | 65535 | 2-4-8-1-3 | 6 (ou absent) | 1460 | 64 | | Windows (sans TS) | 8192 | 2-4-1-3 | 2 | 1460 | 128 | **Détection VPN/Tunnel** : - MSS = 1380 : présence d'un tunnel VPN (overhead d'encapsulation ≈ 80 octets) - MSS = 1360 : double encapsulation (VPN sur VPN, ou VPN sur réseau mobile) - MSS = 1452 : réseau PPPoE (DSL, certains opérateurs mobiles) **Signatures de scanners** : | Outil | Window Size | Options TCP | MSS | Comportement caractéristique | |-------|------------|-------------|-----|------------------------------| | Masscan | 1024 | 2 (MSS seulement) | 1460 | Pas de SACK, pas de timestamps, pas de window scale | | ZMap | 65535 | 2-4-8-1-3 | 1460 | Fenêtre maximale | | Nmap | 1024 | 2-4-8-1-3 | 1460 | Options standard mais fenêtre minimale | | Nmap (SYN stealth) | 1024 | 2-4-1-3 | variable | Pas de timestamps | **Détection de carriers mobiles** : - AT&T : MSS = 1340 (réseau LTE avec overhead GTP) - Verizon : MSS = 1392 (optimisation réseau propriétaire) - T-Mobile : MSS = 1420 **Features TCP capturées** : `tcp_meta_window_size`, `tcp_meta_mss`, `tcp_meta_window_scale`, `tcp_meta_options`, `ip_meta_ttl` **Features dérivées** : `mss_mobile_mismatch` (MSS mobile mais UA desktop), `no_window_scale_ratio` (ratio de connexions sans Window Scale — obsolète ou stack TCP minimal), `avg_ttl`, `ttl_std`, `ip_df_variance` #### 2.2.3 Fingerprinting TLS avancé Au-delà du JA4 de base, plusieurs signaux TLS dérivés améliorent la précision de classification : **Ratio de diversité JA3** : `count(distinct JA3) / count(distinct JA4)`. Pour un navigateur légitime, le ratio est proche de 1 (même JA4 stable, légère variation JA3 due à GREASE). Un bot qui randomise les valeurs GREASE pour contourner la détection JA3 produit un ratio élevé (5–20 JA3 différents pour le même JA4), révélant la stratégie d'évasion elle-même. **Absence d'ALPN** (`is_alpn_missing`) : l'absence de l'extension ALPN dans le ClientHello signale un client non-navigateur. Tous les navigateurs modernes (Chrome, Firefox, Safari) incluent systématiquement ALPN avec `h2` et `http/1.1` depuis 2016. Les bibliothèques TLS minimalistes (Go `crypto/tls` configurée minimalement, certaines versions d'OpenSSL sans configuration ALPN) omettent cette extension. Valeur de précision élevée. **Divergence SNI↔Host** (`sni_host_mismatch`) : détecte une discordance entre le nom d'hôte dans le SNI TLS et la valeur de l'en-tête HTTP `Host`. Cette discordance peut indiquer : - **Domain fronting** : technique d'évasion de censure où le SNI pointe vers un CDN de confiance (ex. `cloudfront.net`) mais l'en-tête HTTP Host pointe vers le domaine cible réel, masquant la destination dans la couche TLS. Bloqué par la plupart des CDN majeurs depuis 2018 mais encore utilisé dans certains contextes. - **Proxy mal configuré** : un proxy réécrit l'en-tête Host sans mettre à jour la session TLS. - **Outil d'automatisation** : certains frameworks HTTP ne synchronisent pas SNI et Host correctement. **Timing SYN→ClientHello** : le délai entre le paquet SYN TCP et le message ClientHello TLS mesure le temps nécessaire au client pour initier la négociation TLS après l'établissement de la connexion TCP. Variance et coefficient de variation (`tcp_jitter_variance`, `syn_timing_cv`) : les scripts automatisés montrent une variance proche de zéro (délai constant, déterminé par le code), les humains montrent une variance naturelle (réseau + délai de clavier/application). --- ### 2.3 Analyse comportementale HTTP #### 2.3.1 Signaux d'en-têtes HTTP Les navigateurs web modernes envoient un ensemble précis d'en-têtes HTTP à chaque requête. L'absence ou la déformation de ces en-têtes constitue un signal de détection fiable pour les clients non-navigateurs. | En-tête | Signification | Comportement navigateur | Signal bot si absent/anormal | |---------|---------------|------------------------|------------------------------| | `Accept-Language` | Préférence de langue, ex. `fr-FR,fr;q=0.9,en;q=0.8` | Toujours présent | Souvent absent dans les scripts | | `Accept-Encoding` | Encodages supportés : `gzip, deflate, br` | Inclut `br` (Brotli, [RFC 7932](https://www.rfc-editor.org/rfc/rfc7932)) depuis Chrome 49 (2016) | Absence de `br` = client pré-2016 ou non-navigateur | | `Sec-Fetch-Site` | Origine de la requête : `same-origin`, `cross-site`, `none` | Présent depuis Chrome 76, Firefox 90 | Jamais généré par les non-navigateurs | | `Sec-Fetch-Mode` | Mode de la requête : `navigate`, `cors`, `no-cors`, `same-origin` | Présent, valeur sémantique stricte | Signal d'authenticité très fort si cohérent | | `Sec-Fetch-Dest` | Destination : `document`, `script`, `image`, `style`, etc. | Présent, cohérent avec le type de ressource | Incohérence = navigation simulée | | `Sec-Fetch-User` | Initié par l'utilisateur : `?1` | Présent sur les navigations top-level utilisateur | Absent sur requêtes programmatiques | | `Sec-CH-UA` | Client Hints UA : `"Chromium";v="120", "Not(A:Brand";v="24"` | Envoyé par Chromium depuis Chrome 89 | Absent dans les non-Chromium | | `Sec-CH-UA-Mobile` | Indicateur mobile : `?0` (desktop) ou `?1` (mobile) | Cohérent avec l'UA et le type d'appareil | `?1` avec UA desktop = mismatch | | `Sec-CH-UA-Platform` | Plateforme : `"Windows"`, `"macOS"`, `"Linux"`, `"Android"` | Cohérent avec l'OS dans l'UA | Incohérence détectable | | `Cookie` | Cookies de session et tracking | Maintenu automatiquement par le navigateur | Absent pour les bots stateless | | `Referer` | URL d'où provient la requête | Présent lors de la navigation dans une page | Absent = accès direct (typique des bots) | | Ordre des en-têtes | Séquence fixe propre à chaque implémentation | Chrome: Host, Connection, sec-ch-ua... | Ordre non-Chrome révèle la bibliothèque réelle | **Fetch Metadata** : les en-têtes `Sec-Fetch-*` sont définis dans la [spécification W3C Fetch Metadata](https://www.w3.org/TR/fetch-metadata/) et envoyés automatiquement par les navigateurs Chromium (depuis Chrome 76, 2019) et Firefox (depuis Firefox 90, 2021). Ils fournissent au serveur des informations sur le contexte de la requête : est-elle initiée par l'utilisateur ? Par un script cross-origin ? Vers une iframe ? Ces en-têtes ne sont jamais générés par des bibliothèques HTTP non-navigateur (Python requests, curl, Go net/http, Node.js fetch de base). **Brotli** : algorithme de compression [RFC 7932](https://www.rfc-editor.org/rfc/rfc7932) développé par Google, plus efficace que gzip (15–25 % de compression supplémentaire). Supporté nativement par tous les navigateurs modernes depuis 2016. L'absence de `br` dans `Accept-Encoding` indique fortement un client non-navigateur ou une très vieille version. **Client Hints** : mécanisme [MDN Client Hints](https://developer.mozilla.org/en-US/docs/Web/HTTP/Client_hints) permettant aux navigateurs Chromium d'envoyer des informations structurées sur le client (UA, plateforme, mobilité) à travers des en-têtes `Sec-CH-*` standardisés. Le `ua_ch_mismatch` détecte une incohérence entre `Sec-CH-UA-Mobile: ?1` (mobile) et un User-Agent desktop, ou l'absence de `Sec-CH-UA` dans un UA se revendiquant Chrome récent. **Features extraites** : `has_accept_language`, `has_cookie`, `has_referer`, `sec_fetch_absence_rate` (ratio de requêtes sans Sec-Fetch), `generic_accept_ratio` (ratio Accept générique `*/*`), `missing_accept_enc_ratio`, `modern_browser_score`, `ua_ch_mismatch`, `header_count`, `header_order_confidence` #### 2.3.2 Patterns de navigation Les patterns de navigation à travers un site web distinguent les comportements humains des comportements automatisés selon plusieurs dimensions : **asset_ratio** : `count_assets / hits` où count_assets = requêtes vers CSS, JavaScript, images, fonts. Un navigateur humain charge typiquement 60 à 80 % de ressources statiques pour chaque page HTML visitée (chargement du template, des scripts, des images). Un scraper qui n'extrait que le HTML produit un `asset_ratio` proche de 0. Valeurs typiques : navigateur humain 0.6–0.8, scraper HTML 0.01–0.05, bot API 0. **direct_access_ratio** : ratio de requêtes sans en-tête `Referer`. Les humains naviguent en suivant des liens (Referer présent sauf première page), les bots accèdent directement aux URLs. Valeurs : humain 0.1–0.3, bot de scraping 0.7–1.0. **path_diversity_ratio** : `uniq_paths / hits`. Un crawler systématique accède à une URL différente à chaque requête (ratio ≈ 1). Un bot répétitif accède toujours au même chemin (ratio ≈ 0.01). La navigation humaine est intermédiaire (0.3–0.8) car les utilisateurs visitent plusieurs pages mais revisitent parfois certaines. **url_depth_variance** : variance de la profondeur des URLs (nombre de `/` dans le chemin). Un crawler systématique parcourt méthodiquement une profondeur fixe (variance faible). La navigation humaine génère une variance naturelle. #### 2.3.3 Brute-force et credential stuffing Les attaques d'authentification par force brute et credential stuffing (utilisation de couples identifiant/mot de passe volés) produisent des patterns comportementaux distincts : **fuzzing_index** : `uniq_query_params / uniq_paths`. Un fuzzer d'API teste de nombreuses valeurs de paramètres sur un nombre restreint d'endpoints (ratio élevé, ex. 50 paramètres différents sur `/api/search` → ratio 50). La navigation normale génère environ autant de chemins distincts que de paramètres distincts (ratio ≈ 1). **post_ratio** : `count_POST / hits`. Un trafic normal a un faible ratio de requêtes POST (soumissions de formulaires). Un attaquant en credential stuffing maintient un ratio POST élevé (0.5–1.0) car chaque tentative d'authentification est un POST. **count_login_post** : compteur direct des requêtes POST vers les endpoints d'authentification (`/login`, `/signin`, `/auth`, `/wp-login.php`, `/account/login`). Valeurs : humain 0–2 par session, credential stuffer 100–10 000. **login_post_concentration** : `count_login_post / total_POST` — mesure si les POSTs se concentrent sur les endpoints de connexion, distinguant un utilisateur qui utilise beaucoup de formulaires d'un attaquant ciblant uniquement l'authentification. --- ### 2.4 Apprentissage automatique pour la détection d'intrusions #### 2.4.1 Approches supervisées et leurs limites **XGBoost (eXtreme Gradient Boosting)** XGBoost ([Chen & Guestrin, 2016](https://arxiv.org/abs/1603.02754)) est un algorithme d'ensemble d'arbres de décision entraînés séquentiellement (boosting). Principe : chaque arbre est entraîné pour corriger les erreurs résiduelles de l'ensemble des arbres précédents, en minimisant un gradient de la fonction de perte. La "descente de gradient" s'effectue dans l'espace des fonctions (arbres) plutôt que dans l'espace des paramètres. **Mécanisme technique** : 1. Initialisation : prédiction initiale constante f0(x) = argmin Σ L(yi, γ) 2. Pour chaque itération m = 1,...,M : - Calcul des résidus pseudo-gradients : rim = -[∂L(yi, F(xi)) / ∂F(xi)] pour chaque xi - Entraînement d'un arbre hm sur les résidus rim - Mise à jour : F_m(x) = F_{m-1}(x) + η × hm(x) où η est le learning rate 3. La prédiction finale est : F(x) = f0(x) + Σ η × hm(x) **Optimisations XGBoost** : - **Parallélisation** : la construction de chaque arbre est parallélisée par sous-échantillonnage de colonnes (colsample_bytree) et par tri par histogramme (histogram approximation du split optimal) - **Régularisation L1/L2** : termes de régularisation ajoutés à la fonction objectif pour contrôler la complexité des arbres - **Hyperparamètres clés** : `max_depth` (profondeur maximale des arbres), `n_estimators` (nombre d'arbres), `learning_rate` (η), `subsample` (fraction des lignes par arbre), `colsample_bytree` (fraction des features par arbre) **Performances** : [Osama et al., 2025 (arXiv:2512.23610)](https://arxiv.org/abs/2512.23610) rapportent 99,59 % de précision et une inférence en microsecondes pour XGBoost sur des features comportementales de bot detection, contre 10–14 % de blocage pour les WAF à règles face aux mêmes payloads augmentés par LLM. **Limites des approches supervisées** : - **Concept drift** : un modèle entraîné sur des bots de 2024 peut être aveugle aux nouvelles techniques de 2025 - **Rareté des étiquettes** : annoter manuellement des millions de sessions HTTP est coûteux et sujet à erreur - **Bruit des étiquettes** : les labels fournis par les analystes SOC contiennent des erreurs systématiques (faux positifs mal corrigés, biais de confirmation). Ces étiquettes bruitées empoisonnent le modèle supervisé — un problème bien documenté par [Northcutt et al., 2021 (Cleanlab)](https://arxiv.org/abs/1911.00068) qui montre que les jeux de données réels contiennent 8 à 20 % de labels incorrects. Pour mitiger ce risque, notre pipeline intègre un filtre Cleanlab avant l'apprentissage incrémental du HAT (détail §3.8). - **Biais de jeu de données** : les modèles entraînés sur des données de laboratoire (CICIDS2017, NSL-KDD) généralisent mal au trafic en production, comme documenté dans la littérature sur les benchmarks de détection d'intrusions - **Attaque par évasion adversariale** : un attaquant ayant accès ou connaissance du modèle peut crafting des sessions qui maximisent le score de légitimité #### 2.4.2 Approches semi-supervisées **Isolation Forest (Liu et al., 2008)** [Isolation Forest (Liu, Ting, Zhou, ICDM 2008)](https://cs.nju.edu.cn/zhouzh/zhouzh.files/publication/icdm08b.pdf) est un algorithme de détection d'anomalies basé sur la notion d'isolation aléatoire. Insight fondamental : les anomalies, étant rares et différentes des points normaux, sont isolées plus rapidement (en moins de partitions récursives) que les points normaux. **Algorithme** : 1. Pour chaque arbre d'isolation (n_estimators arbres) : - Sélectionner aléatoirement ψ points (sous-échantillon) - Construire récursivement une partition binaire : (a) choisir une feature aléatoire q, (b) choisir un seuil p uniformément entre min(q) et max(q), (c) partitionner en q ≤ p et q > p, jusqu'à isolation de chaque point 2. Le score d'anomalie d'un point x est inversement proportionnel à la longueur moyenne du chemin d'isolation : ``` score(x, n) = 2^( -E[h(x)] / c(n) ) ``` où E[h(x)] est la longueur de chemin moyenne à travers tous les arbres, et c(n) = 2H(n-1) - 2(n-1)/n est la longueur de chemin moyenne attendue dans une BST (Binary Search Tree) unsuccessful, avec H(i) = ln(i) + 0.5772 (constante d'Euler-Mascheroni). Un score proche de 1 indique une anomalie (chemin court, isolé rapidement) ; un score proche de 0.5 est normal ; un score < 0.5 est clairement normal. **Extended Isolation Forest (EIF)** [Extended Isolation Forest (Hariri, Kind, Brunner, IEEE TKDE 2021)](https://ieeexplore.ieee.org/document/8888179) résout un problème fondamental de l'Isolation Forest standard dans les espaces à haute dimension. **Problème des « fantômes de clusters » (ghost clusters)** : l'IF standard utilise des coupes axis-alignées (hyperplans parallèles aux axes des features). En haute dimension (> 10 features), cette stratégie produit des artéfacts géométriques : des zones de l'espace des features sans données reçoivent artificellement des scores d'anomalie bas, car les coupes parallèles aux axes ont tendance à partitionner les régions denses (là où se concentrent les données normales) plutôt que les régions vides aux frontières. Ces zones vides — les « fantômes de clusters » — sont des minima artificiels du score d'anomalie créés par la géométrie des hyperplans axis-alignés, et non par la présence réelle de données normales. **Solution EIF** : remplacer les coupes axis-alignées par des hyperplans à pente aléatoire. À chaque nœud de l'arbre : 1. Choisir un vecteur normal aléatoire n ∈ ℝ^d (uniform sur la sphère unité) 2. Choisir un point d'interception aléatoire p ∈ [min(X·n), max(X·n)] 3. Partitionner : {x : x·n ≤ p} et {x : x·n > p} Résultat : les coupes ne sont plus liées aux axes, éliminant les artéfacts géométriques. Les scores d'anomalie deviennent cohérents et fiables, particulièrement dans les espaces à 47–59 dimensions de notre architecture. **Relation IF/EIF** : l'IF standard est un cas particulier de EIF avec `extension_level=0` (vecteur normal aligné sur un seul axe aléatoire). **Implémentation** : bibliothèque [isotree (Cortes, 2021+)](https://github.com/david-cortes/isotree) — implémentation C++ native avec interface Python. Paramètres de production : `ntrees=300`, `contamination=0.001`. **Calibration des scores** : isotree retourne un score ∈ [0, 1] où > 0.5 = anomalie. Transformation pour compatibilité sklearn : `sklearn_equiv = 0.5 - isotree_score` (négatif = anomalie). Le modèle est sérialisé avec joblib accompagné des statistiques de baseline (quantiles par feature : p10, p25, p50, p75, p90) pour la détection de dérive. **Architecture bifurquée EIF** : Deux modèles EIF s'exécutent en parallèle à chaque cycle de 300 secondes : - **Modèle Complet** (≈ 45 features, L3→L7) : appliqué sur les sessions pour lesquelles ja4ebpf a pu corréler les métadonnées TCP/TLS avec la requête HTTP (données L3/L4 disponibles). Inclut toutes les features des familles F1–F7. - **Modèle Applicatif** (≈ 35 features, L7 uniquement) : appliqué sur les sessions dont les données TCP/TLS sont absentes — trafic passant par un CDN ou proxy qui ne permet pas la corrélation TCP/TLS. Utiliser les features TCP/TLS imputées à zéro pour ce modèle introduirait un biais systématique. La bifurcation est justifiée par le fait que les features TCP/TLS ne sont disponibles que lorsque ja4ebpf a corrélé la connexion réseau avec la requête HTTP. Imputer ces features à zéro pour le trafic non corrélé créerait un signal artificiel (zéro n'est pas neutre pour un EIF — il est interprété comme une valeur réelle). #### 2.4.2b Normalizing Flows (NF) et détection d'anomalies par vraisemblance **Limites de l'erreur de reconstruction MSE** L'approche par autoencodeur, bien que répandue, repose sur un postulat fragile : qu'un réseau entraîné uniquement sur des données normales reconstruira mal les anomalies. En pratique, les autoencodeurs à capacité suffisante peuvent généraliser et reconstruire correctement des échantillons hors distribution, rendant le score MSE peu fiable comme mesure d'anomalie. De plus, l'erreur de reconstruction n'a pas d'interprétation probabiliste — elle ne permet pas de quantifier dans quelle mesure un échantillon est improbable sous la distribution des données normales. **Normalizing Flows : estimation de densité par changement de variable** Un Normalizing Flow est une bijection apprise $f_\theta : \mathcal{X} \to \mathcal{Z}$ qui transforme l'espace des données $\mathcal{X}$ vers un espace latent $\mathcal{Z}$ de même dimension, typiquement muni d'une distribution simple (gaussienne standard $\mathcal{N}(0, I)$). La log-vraisemblance exacte d'un échantillon $x$ s'obtient par la formule du changement de variable : ``` log p(x) = log p_z(f_θ(x)) + log|det J_fθ(x)| ``` où $J_{f_\theta}(x)$ est la matrice jacobienne de $f_\theta$ en $x$. Cette formule fournit une estimation de densité exacte (pas de borne inférieure variationnelle comme dans les VAE), ce qui est mathématiquement plus rigoureux pour l'estimation de densité. **Architecture RealNVP** : 4 blocs de couplage affine (Dinh et al., 2017), chacun utilisant un sous-réseau MLP (2 couches, 64 neurones, ReLU). Chaque bloc divise les dimensions en deux groupes, transforme la moitié via une fonction affine paramétrée par l'autre moitié, et alterne. Le log-déterminant jacobien se calcule en complexité O(D) (produit des dérivées des facteurs d'échelle), rendant l'entraînement tractable. **Score d'anomalie** : $-\log p(x)$. Les échantillons normaux ont une forte vraisemblance (score faible) ; les anomalies tombent dans les régions de faible densité de $p(x)$ (score élevé). Contrairement au MSE, ce score est une mesure probabiliste directement interprétable. **Avantage fondamental sur EIF** : le NF estime la densité de probabilité jointe $p(x_1, \ldots, x_D)$ sur toutes les features, capturant les corrélations non-linéaires entre features. Un bot utilisant httpcloak peut imiter individuellement chaque feature Chrome (JA4 correct, ALPN correct, en-têtes HTTP corrects) mais leurs inter-corrélations inhabituelles (tcp_jitter_variance × sec_fetch_absence_rate × asset_ratio) correspondent à une région de faible densité sous $p(x)$. EIF isole les points extrêmes dans l'espace des features ; le NF quantifie directement leur improbabilité jointe. **Implémentation** : PyTorch + FrEIA (Framework for Easily Invertible Architectures), 4 blocs AllInOneBlock, perte = NLL moyenne, Adam + early stopping. **Kitsune** [Kitsune (Mirsky et al., NDSS 2018)](https://www.ndss-symposium.org/wp-content/uploads/2018/02/ndss2018_03A-3_Mirsky_paper.pdf) est un NIDS (Network Intrusion Detection System) en ligne basé sur un ensemble d'autoencoders légers (KitNET). Architecture : N petits autoencoders (~64 neurones chacun) correspondant à des groupes de features corrélées, dont les erreurs de reconstruction sont agrégées par un autoencodeur de sortie servant de vote non-linéaire. Démonstration que de petits AE sur Raspberry Pi atteignent des performances comparables aux détecteurs hors ligne, validant l'approche d'ensemble d'AE pour la détection d'intrusions en production. **β-VAE** Les Variational Autoencoders (VAE) ajoutent une régularisation KL-divergence structurant l'espace latent. La KL-divergence (divergence de Kullback-Leibler) mesure comment une distribution de probabilité P diffère d'une distribution de référence Q : ``` KL(P||Q) = Σ P(x) × log(P(x)/Q(x)) ``` Pour un VAE, la régularisation KL(q(z|x) || p(z)) pénalise l'encodeur si sa distribution postérieure q(z|x) s'éloigne d'une normale standard p(z) = N(0, I). Score d'anomalie VAE : `-log p(x|z) + KL(q(z|x) || p(z))`. Cette régularisation rend l'espace latent plus lisse et mieux organisé, potentiellement améliorant la qualité du clustering HDBSCAN. **Complémentarité AE + IF** La complémentarité entre IF et AE est un résultat bien établi en détection d'anomalies : | Critère | Isolation Forest (EIF) | Normalizing Flow (NF) | |---------|------------------------|------------------------| | Type d'anomalie détecté | Points isolés dans l'espace des features | Régions de faible densité jointe | | Interprétabilité | ExIFFI (profondeur d'isolation par feature) | Log-vraisemblance par dimension | | Dimensionnalité | Optimal jusqu'à ~100 features | Robuste, même dimension que l'entrée | | Détection d'imitation | Partielle | Plus forte (densité jointe inter-features) | | Espace pour clustering | Non adapté | Espace latent (bijection) idéal pour HDBSCAN | | Coût computationnel | Faible (arbres) | Modéré (réseau de neurones) | **Espace latent NF pour HDBSCAN** : la bijection $f_\theta$ du Normalizing Flow transforme l'espace des features en un espace latent où la distribution est $\mathcal{N}(0, I)$. Le clustering HDBSCAN dans l'espace latent $z = f_\theta(x)$ regroupe les sessions dont la structure (après transformation) est similaire — c'est-à-dire les bots utilisant le même outil ou la même configuration — identifiant ainsi les campagnes coordonnées. #### 2.4.2c Ensembles hybrides supervisé + non-supervisé **Architecture triple-voix** Le système de détection combine trois « voix » complémentaires : ``` ┌─────────────────────┐ │ Vecteur de 85 │ │ features par │ │ session (5min) │ └────────┬────────────┘ │ ┌──────────────┼──────────────┐ │ │ │ ┌─────▼─────┐ ┌─────▼─────┐ ┌────▼──────┐ │ EIF │ │ NF │ │ HAT │ │ (semi- │ │ (Normal- │ │ (River, │ │supervisé) │ │ izing │ │ supervisé │ │ │ │ Flow) │ │ online) │ └─────┬─────┘ └─────┬─────┘ └────┬──────┘ │ │ │ eif_norm nf_norm hat_prob │ │ │ └──────────────┼──────────────┘ │ ┌────────▼────────┐ │ Fusion MLP │ │ non-linéaire │ └────────┬────────┘ │ ┌────────▼────────┐ │ score_final │ │ → étiquette │ └─────────────────┘ ``` **Limites de la fusion linéaire** Une fusion linéaire — combinaison convexe pondérée ou régression logistique — ne peut capturer que des frontières de décision linéaires dans l'espace des scores intermédiaires. Or les signaux EIF, NF et HAT peuvent exhiber des interactions non-linéaires impossibles à modéliser par une combinaison linéaire : ``` Problème XOR des scores : NF élevé ┌────────┐ EIF bas │ BOT ✓ │ ← combinaison inhabituelle = bot confirmé ├────────┤ EIF haut │normal ✗│ ← faux positif fréquent de l'EIF seul └────────┘ NF bas ``` Exemple concret : un bot utilisant un outilHeadless avec un JA4 fingerprint légitime (NF bas) mais un comportement de navigation atypique (EIF élevé). Le HAT peut compenser, mais la fusion linéaire ne peut apprendre la relation *« EIF élevé ET HAT élevé MAIS NF bas = bot »* — elle ne fait que sommer les contributions indépendantes. **Stacking OOF (Out-of-Fold) et MLP méta-modèle** Pour résoudre cette limitation, le système utilise un méta-modèle non-linéaire de type MLP (*Multi-Layer Perceptron*) entraîné via stacking Out-of-Fold : 1. **Prédictions OOF** : les modèles de base (EIF, NF, HAT) produisent des prédictions sur des plis de validation croisée temporelle, garantissant que le méta-modèle n'a jamais vu les données d'entraînement des modèles de base — évitant le surapprentissage (*information leakage*). 2. **Méta-modèle MLP** : un réseau de neurones à 2 couches apprend la fonction de fusion optimale : ``` MetaFusionMLP : [eif, nf, hat] → Linear(3,16) → BatchNorm → ReLU → Dropout(0.2) → Linear(16,1) → Sigmoid → P(bot) ``` - **BatchNorm** : normalise les activations intermédiaires, stabilise l'apprentissage et régularise implicitement — crucial avec peu de données labellisées. - **Dropout(0.2)** : désactive aléatoirement 20% des neurones pendant l'entraînement, prévenant la co-adaptation des poids. - **Early stopping** (patience = 5 epochs) : arrête l'entraînement dès que la loss de validation ne s'améliore plus, évitant le surapprentissage. - **Weight decay** ($\lambda = 10^{-4}$) : pénalité L2 sur les poids du MLP pour une régularisation supplémentaire. Le MLP apprend des frontières de décision non-linéaires dans l'espace 3D `[eif_norm, nf_norm, hat_prob]`, capable de résoudre les patterns XOR et les interactions conditionnelles entre les trois voix. Le système de détection peut ainsi combiner automatiquement les signaux de manière optimale en fonction du type de trafic observé en production. **Calendrier de retraining** : - HAT (supervisé) : apprentissage incrémental à chaque cycle (300s) sur les étiquettes accumulées, après filtrage Cleanlab des labels SOC bruyants (voir ci-dessous) - EIF : toutes les 24 heures ou sur détection ADWIN - NF : continu avec arrêt précoce sur la loss de validation **Filtrage des labels SOC bruyants (Cleanlab)** : Avant chaque cycle d'apprentissage incrémental du HAT, les labels fournis par les analystes SOC sont filtrés via [Cleanlab](https://cleanlab.ai/) ([Northcutt et al., 2021](https://arxiv.org/abs/1911.00068)). Ce framework de *confident learning* identifie les exemples dont l'étiquette est probablement erronée en comparant les prédictions out-of-fold d'un modèle aux labels observés. ```python # 1. Obtenir pred_probs via cross-validation (3 folds) sur les labels accumulés from river import tree quick_model = tree.HoeffdingAdaptiveTreeClassifier() # Les labels accumulés ce cycle sont filtrés avant injection dans le HAT pred_probs = cross_val_predict(quick_model, X_accumulated, y_accumulated, cv=3, method='predict_proba') # 2. Identifier les labels douteux issues = find_label_issues(labels=y, pred_probs=pred_probs) # 3. N'injecter que les labels propres via learn_one() for x_clean, y_clean in clean_samples: hat_model.learn_one(x_clean, y_clean) ``` Ce mécanisme protège le modèle contre l'empoisonnement par des faux positifs mal corrigés ou des biais de confirmation des analystes. Le taux de labels filtrés est loggé pour surveillance. En cas d'échec de Cleanlab (erreur mémoire, dépendance manquante), le pipeline revient aux données brutes sans interruption. #### 2.4.3 Concept Drift : ADWIN et Online Learning **Définition du concept drift** En apprentissage automatique, le concept drift désigne le changement des propriétés statistiques de la variable cible ou de la distribution d'entrée dans le temps, causant une dégradation des prédictions du modèle. Types : - **Drift soudain** : changement abrupt (ex. mise à jour majeure du navigateur modifiant les valeurs JA4) - **Drift graduel** : évolution progressive (ex. changement des patterns de navigation au fil des mois) - **Drift récurrent** : patterns saisonniers (ex. comportements différents le weekend vs. la semaine) - **Drift incrémental** : dérive lente et continue (ex. adaptation progressive des techniques d'évasion) En détection de bots, le drift adversarial est particulièrement critique : les attaquants adaptent délibérément leurs outils pour contourner les modèles déployés. **Limites de l'approche KS + quantile digest** L'approche précédente détectait la dérive en sauvegardant 5 quantiles (p10, p25, p50, p75, p90) par feature à l'entraînement, puis en reconstruisant une distribution synthétique par interpolation de la CDF inverse et en appliquant un test de Kolmogorov-Smirnov entre cette distribution et la distribution courante. Cette méthode souffre de trois lacunes fondamentales : 1. **5 quantiles ne captent pas les distributions bimodales** : les features de trafic web comme `asset_ratio`, `post_ratio` et `orphan_ratio` ont souvent des distributions bimodales (deux populations de trafic distinctes). Cinq points ne suffisent pas à reconstruire fidèlement ces distributions — une dérive dans un mode peut être masquée par la stabilité de l'autre mode. 2. **Reconstruction par interpolation linéaire** : interpoler entre 5 quantiles suppose une distribution unimodale et lisse. Pour les distributions skewed à queue lourde (timing inter-requêtes, taille des payloads), l'interpolation sous-estime systématiquement les valeurs extrêmes, rendant le test KS peu fiable. 3. **Seuil de 30% arbitraire** : le seuil `DRIFT_THRESHOLD = 0.30` (30% de features en dérive) est un hyperparamètre non fondé statistiquement. Il est trop sensible pour les périodes de forte activité (faux positifs) et trop conservateur pour les attaques furtives ne touchant que quelques features. **ADWIN : fenêtre glissante adaptative** [ADWIN (ADaptive WINdowing, Bifet & Gavalda, 2007)](https://dl.acm.org/doi/10.1145/1242572.1242660) résout ces problèmes en maintenant une fenêtre de longueur variable sur le flux de données. Le principe : 1. **Fenêtre adaptative** : ADWIN maintient une fenêtre $W$ de valeurs récentes. La taille de $W$ s'ajuste automatiquement — elle grandit quand la distribution est stable et rétrécit quand un changement est détecté. 2. **Test de Hoeffding** : pour chaque coupe possible $W = W_0 \cup W_1$, ADWIN compare les moyennes $\hat{\mu}_0$ et $\hat{\mu}_1$ des deux sous-fenêtres. Si la différence dépasse la borne de Hoeffding : $$|\hat{\mu}_0 - \hat{\mu}_1| \geq \sqrt{\frac{1}{2m} \ln\frac{4}{\delta}}$$ où $m = \frac{1}{1/|W_0| + 1/|W_1|}$ et $\delta$ est le paramètre de confiance, alors un changement est détecté et la sous-fenêtre la plus ancienne est supprimée. 3. **Pas de seuil arbitraire** : la détection repose uniquement sur la borne de Hoeffding (garantie probabiliste), paramétrée par $\delta$ (défaut : 0.002). Aucun seuil de « 30% de features en dérive » n'est nécessaire au niveau de chaque feature — seul le nombre de features driftant simultanément déclenche le retraining global. **Architecture de monitoring ADWIN** : ``` Cycle n (300s) │ ├── Calculer μ_f pour chaque feature f sur le trafic baseline │ ├── ADWIN_f.update(μ_f) pour chaque feature │ └── Fenêtre interne W_f ajustée automatiquement │ ├── ADWIN_f.detected_change() ? │ └── Si oui → feature f marquée « en dérive » ce cycle │ └── Si > 30% features en dérive → flag retraining EIF/NF Si > 50% features en dérive → alerte ADVERSARIAL_DRIFT ``` **Online Learning : Hoeffding Adaptive Tree** Le retraining hebdomadaire par batch (`XGBClassifier.fit()` sur l'ensemble des labels accumulés) est remplacé par un apprentissage incrémental via [River](https://riverml.xyz/), une bibliothèque spécialisée en stream mining. Le modèle utilisé est le `HoeffdingAdaptiveTreeClassifier` (HAT) : - **Arbre de décision incrémental** : construit l'arbre de décision progressivement, un exemple à la fois via `learn_one(x, y)`. À chaque split, le test de Hoeffding garantit que le split choisi est (probablement) le même que celui qu'un arbre batch aurait choisi avec les mêmes données. - **Adaptatif** : utilise des estimateurs de fenêtre ADWIN à chaque nœud pour remplacer les statistiques obsolètes — le modèle s'adapte automatiquement au concept drift sans retraining explicite. - **Apprentissage par cycle** : à chaque cycle de 300s, les nouveaux labels accumulés sont injectés un par un via `learn_one()`. Le modèle s'améliore continuellement, rendant le lourd retraining hebdomadaire obsolète. ``` Cycle 300s │ ├── Charger HAT sérialisé (pickle) │ ├── Pour chaque label accumulé ce cycle : │ model.learn_one({feature: value, ...}, label) │ ├── Persister HAT mis à jour │ └── Prédiction : model.predict_proba_many(df) → P(bot) ``` Le passage au stream mining élimine trois problématiques majeures du batch training : - **Latence de mise à jour** : de hebdomadaire à chaque cycle (300s) - **Coût mémoire** : plus besoin de charger 50 000 labels en RAM - **Stale model** : le modèle est toujours à jour par rapport au concept courant **Validation gate** : conservée — si le taux d'anomalie sur le jeu de validation dépasse 20% après retraining EIF/NF, le nouveau modèle est rejeté et le modèle précédent conservé. **Quantification d'incertitude par Deep Ensembles** La détection adversariale par ADWIN reposait sur l'heuristique suivante : si plus de 50% des features driftent simultanément, le drift est qualifié d'adversarial. Cette heuristique est non fondée — un pic de légitime trafic (ex. mise à jour navigateur majeure) peut déclencher un drift massif sur de nombreuses features sans pour autant être adversarial. À l'inverse, une attaque furtive ne touchant que quelques features ne serait jamais détectée. Cette heuristique est remplacée par une mesure d'incertitude épistémique via **Deep Ensembles** ([Lakshminarayanan et al., 2017](https://arxiv.org/abs/1612.01474)) : le Normalizing Flow unique est remplacé par un ensemble de $M=5$ modèles indépendants, chacun entraîné sur un échantillon bootstrap (avec remise) de la baseline humaine. L'incertitude est mesurée par la variance inter-modèles : $$\sigma^2(x) = \frac{1}{M} \sum_{m=1}^{M} \left( -\log p_m(x) - \overline{-\log p(x)} \right)^2$$ La logique de détection repose sur l'intuition suivante : - **Dérive organique** (changement naturel du trafic) : les 5 modèles s'accordent sur la nouveauté → variance faible. Tous les manifolds ont capturé les mêmes structures dans la baseline, donc un nouveau pattern légitime est traité de manière cohérente. - **Dérive adversariale** (évasion délibérée) : les 5 modèles ne s'accordent pas → variance qui explose. Un échantillon adversarial tombe dans une région de l'espace où chaque manifold a appris une frontière légèrement différente (diversité induite par le bootstrap), produisant des scores de vraisemblance très dispersés. Le seuil `NF_UNCERTAINTY_THRESHOLD` (défaut : 1.0) est appliqué sur $\sigma^2(x)$ : tout échantillon au-dessus est tagué `is_adversarial_drift = True`. Cette approche est fondée statistiquement (variance sur un ensemble) et ne dépend pas d'un seuil arbitraire sur le nombre de features en drift. La modélisation des phases d'attaque (Reconnaissance → Mouvement latéral → Intrusion → Exfiltration) par des modèles d'état-espace ou des processus de Markov cachés constitue une piste de recherche. L'enrichissement du clustering HDBSCAN avec ce signal de phase permettrait de distinguer des campagnes en phase de reconnaissance de campagnes en phase d'exploitation active. La modélisation des phases d'attaque par des modèles d'état-espace ou des processus de Markov cachés constitue une piste de recherche pour distinguer les campagnes en phase de reconnaissance de celles en phase d'exploitation active. #### 2.4.5 Explicabilité par SHAP et ExIFFI **SHAP (SHapley Additive exPlanations)** [SHAP (Lundberg & Lee, 2017, shap.readthedocs.io)](https://shap.readthedocs.io/) est un framework d'explicabilité locale basé sur la théorie des jeux coopératifs (valeurs de Shapley). La contribution d'une feature i à la prédiction pour un exemple x est définie comme sa contribution marginale moyenne sur toutes les ordonnances possibles des features : ``` φi(f, x) = Σ_{S ⊆ F\{i}} [|S|!(|F|-|S|-1)!/|F|!] × [f(S∪{i}) - f(S)] ``` où F est l'ensemble de toutes les features, S est un sous-ensemble, et f(S) est la prédiction du modèle en utilisant seulement les features dans S (les autres étant marginalisées). **SHAP TreeExplainer** est une implémentation exacte et efficace pour les modèles basés sur des arbres (XGBoost, Random Forest, LightGBM) en complexité O(TLD²) où T = nombre d'arbres, L = nombre de feuilles, D = profondeur maximale — beaucoup plus rapide que l'estimation par permutation (O(2^|F|)). **ExIFFI (Extended Isolation Forest Feature Importance)** L'importance des features par profondeur d'isolation (approche de type ExIFFI) est une méthode native d'importance des features pour EIF, basée sur la profondeur moyenne d'isolation par feature. Principe : une feature ayant une profondeur d'isolation moyenne faible (isole rapidement les anomalies) est plus importante pour la détection. Activé comme fallback lorsque SHAP n'est pas disponible. Comparé aux top-5 SHAP dans l'interface SOC. **Log-vraisemblance NF par dimension** : via la décomposition du log-déterminant jacobien par bloc de couplage, il est possible d'identifier quelles dimensions contribuent le plus à la faible vraisemblance d'un échantillon. Cela fournit une explicabilité locale pour chaque session anormale : les dimensions dont le log-déterminant est le plus négatif correspondent aux features les plus anormales sous la distribution apprise. --- **Approches émergentes : GNN et Transformers** L'analyse de séquences de navigation et la détection de flottes distribuées bénéficient également d'approches plus récentes : - **Graph Neural Networks (GNN)** : les GNN opèrent directement sur des graphes de co-occurrence (par exemple IP↔IP via co-occurrence JA4×ASN), apprenant des embeddings de nœuds qui capturent la structure topologique des flottes tout en intégrant les features comportementales de chaque IP. Cette architecture utilise un GNN **GraphSAGE** (§5.2) pour la détection de flottes, combinant la structure du graphe de co-occurrence et le profil ML de chaque IP dans un espace latent 32D clusterisé par HDBSCAN. - **Transformers pour l'analyse de séquences** : les architectures Transformer (self-attention) permettent de modéliser les dépendances longues dans les séquences de chemins ou les séries temporelles d'intervalles inter-requêtes, capturant des patterns que les chaînes de Markov d'ordre 1 ne peuvent pas représenter. Cette architecture intègre ces approches avancées : un **Session Transformer** (§5.1) pour l'encodage contextuel des séquences de navigation et un **GraphSAGE** (§5.2) pour la détection de flottes, en complément des méthodes de clustering HDBSCAN et du filtrage Cleanlab pour la robustesse des labels. --- ### 2.5 Détection côté client (Browser Fingerprinting) #### 2.5.1 JavaScript Challenges Les challenges JavaScript constituent la principale approche de détection côté client : injection de code JS dans les pages pour collecter Canvas fingerprint, WebGL renderer, propriétés du navigateur, timing d'exécution JS, et soumettre ces données au serveur pour scoring. **Principaux acteurs** : Cloudflare Turnstile, DataDome, PerimeterX (HUMAN Security), Akamai Bot Manager, Shape Security. **Limites** : - **Contournement par navigateurs sans tête patchés** : BotBrowser et CloakBrowser annoncent « 30/30 tests passés » sur les suites de détection standard comme [botcheck.me](https://botcheck.me), [creepjs](https://abrahamjuliot.github.io/creepjs/), et les challenges Cloudflare. Ces outils patchent Chromium au niveau binaire pour supprimer les artefacts d'automatisation (`navigator.webdriver`, propriétés `__proto__` de l'injection Selenium, etc.) et spoofer les valeurs Canvas/WebGL. - **Inapplicabilité aux APIs** : les endpoints d'API ne peuvent pas injecter de JavaScript. Toute protection API repose nécessairement sur des signaux passifs côté serveur. - **Pénalité de latence** : le challenge ajoute une requête supplémentaire (et parfois un délai intentionnel de preuve de travail) à la première visite, dégradant l'expérience utilisateur. **Position de cette architecture** : 100 % passive, aucun challenge JavaScript. L'ensemble des signaux de détection provient de l'analyse des paquets réseau et des en-têtes HTTP. #### 2.5.2 FingerprintJS BotD [FingerprintJS BotD](https://github.com/fingerprintjs/BotD) est une bibliothèque JavaScript côté client qui détecte les frameworks d'automatisation en inspectant : - `navigator.webdriver` : propriété mise à `true` par Selenium et certaines versions de Puppeteer - Artefacts Phantom (`window.__phantom`, `window.callPhantom`) - Artefacts Selenium (`window.selenium`, `document.__webdriver_evaluate`) - Propriétés de prototypes modifiées par les injections automatisées **Limitation** : contournable par des patches bas niveau comme [rebrowser-patches](https://github.com/rebrowser/rebrowser-patches), qui modifient directement le runtime V8 et les propriétés exposées par Chromium. #### 2.5.3 Fingerprinting HTTP/2 passif côté serveur **HTTP/2 : fondements du protocole** HTTP/2 ([RFC 7540](https://www.rfc-editor.org/rfc/rfc7540), remplacé par [RFC 9113](https://www.rfc-editor.org/rfc/rfc9113) en 2022) est un protocole binaire multiplexé remplaçant HTTP/1.1 pour les connexions TLS. Caractéristiques fondamentales : **Framing binaire** : toute communication HTTP/2 est découpée en frames de type : HEADERS, DATA, SETTINGS, WINDOW_UPDATE, PRIORITY, RST_STREAM, PUSH_PROMISE, PING, GOAWAY, CONTINUATION. Chaque frame a un en-tête de 9 octets contenant la longueur, le type, les flags et l'identifiant de stream. **Multiplexage** : plusieurs streams HTTP/2 coexistent sur une seule connexion TCP, chacun identifié par un stream ID (impair pour les streams initiés par le client : 1, 3, 5...). Élimine le head-of-line blocking de HTTP/1.1 pipelines. **Compression d'en-têtes HPACK** : [RFC 7541](https://www.rfc-editor.org/rfc/rfc7541) définit HPACK, qui compresse les en-têtes HTTP/2 via : 1. **Table statique** : 61 paires nom/valeur prédéfinies (ex. entrée 1 = `:authority`, entrée 5 = `:path /`). Référencées par index 1–61. 2. **Table dynamique** : paires ajoutées dynamiquement au fur et à mesure de la connexion, référencées par index 62+. 3. **Codage Huffman** : pour les valeurs littérales, encodage Huffman réduit la taille de 20–30 %. Résultat : réduction de la taille des en-têtes de 85–88 % par rapport à HTTP/1.1. **Pseudo-headers** : HTTP/2 remplace la ligne de requête HTTP/1.1 (`GET /path HTTP/1.1`) par quatre pseudo-headers préfixés par `:` : `:method`, `:path`, `:scheme`, `:authority`. **Contrôle de flux (Flow Control)** : [RFC 7540 §5.2](https://www.rfc-editor.org/rfc/rfc7540#section-5.2) empêche un émetteur rapide de saturer un récepteur lent. Deux niveaux : par stream (stream-level) et par connexion (connection-level). Les frames WINDOW_UPDATE incrémentent la fenêtre de contrôle de flux d'un montant spécifié. **Fingerprinting HTTP/2 passif** L'approche de fingerprinting passif HTTP/2 ([Bartlett, Cloudflare, 2023](https://blog.cloudflare.com/cloudflare-bot-management-machine-learning-and-more/), [Akamai Client Fingerprint](https://www.akamai.com/)) extrait des signaux depuis les frames HTTP/2 reçues par le serveur immédiatement après l'établissement de la connexion. **Avantage fondamental** : le fingerprint HTTP/2 est produit par la couche transport, en dessous du niveau applicatif. Un outil d'évasion doit implémenter une pile HTTP/2 complète et fidèle à un navigateur cible pour l'éviter — effort d'ingénierie significativement supérieur à la simple rotation d'un User-Agent. **Frame SETTINGS (RFC 7540 §6.5)** : Envoyée immédiatement par le client après le préambule de connexion (`PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n`). Contient des paires clé-valeur identifiant les paramètres de la session HTTP/2 : | ID | Nom | Signification | |----|-----|---------------| | 1 | HEADER_TABLE_SIZE | Taille maximale de la table de compression HPACK (octets) | | 2 | ENABLE_PUSH | Activation du Server Push HTTP/2 (0=désactivé, 1=activé) | | 3 | MAX_CONCURRENT_STREAMS | Nombre maximum de streams simultanés | | 4 | INITIAL_WINDOW_SIZE | Taille initiale de la fenêtre de contrôle de flux par stream (octets) | | 5 | MAX_FRAME_SIZE | Taille maximale des frames (octets, min 16384) | | 6 | MAX_HEADER_LIST_SIZE | Taille maximale de la liste d'en-têtes (octets) | | 8 | ENABLE_CONNECT_PROTOCOL | [RFC 8441](https://www.rfc-editor.org/rfc/rfc8441) - WebSocket over HTTP/2 | Valeurs caractéristiques par implémentation : | Client | HEADER_TABLE_SIZE | ENABLE_PUSH | MAX_CONCURRENT_STREAMS | INITIAL_WINDOW_SIZE | MAX_FRAME_SIZE | MAX_HEADER_LIST_SIZE | |--------|-------------------|-------------|------------------------|---------------------|----------------|----------------------| | Chrome 119+ | 65536 | 0 | 1000 | 6291456 | 16384 | 262144 | | Firefox 90+ | 65536 | 0 | 100 | 131072 | 16384 | absent | | Safari 15+ | 4096 | 0 | 100 | 2097152 | 16384 | absent | | curl 7.x | 0 (absent) | — | — | 0 (absent) | — | — (min settings) | | Python httpx | 65536 | 0 | 100 | 65535 | 16384 | absent | | Go net/http | 4096 | 0 | 250 | 1048576 | 16384 | absent | | Node.js http2 | 4096 | 0 | 100 | 65535 | 16384 | absent | **WINDOW_UPDATE** : Après l'échange SETTINGS, le client envoie une frame WINDOW_UPDATE incrémentant la fenêtre de contrôle de flux de connexion. Cette valeur est fixe par implémentation et constitue un signal très discriminant : | Client | Incrément WINDOW_UPDATE | |--------|------------------------| | Chrome 119+ | 15 663 105 (± 1 000) | | Firefox 90+ | 12 517 377 (± 1 000) | | Safari 15+ | 10 420 225 (± 1 000) | | curl | absent (0) | | Python httpx | absent (0) | | Go net/http | 1 073 676 289 (± 1 000) | | Node.js http2 | 65535 (valeur par défaut) | **Ordre des pseudo-headers** : L'ordre dans lequel les pseudo-headers `:method`, `:authority`, `:scheme`, `:path` apparaissent dans la première frame HEADERS est fixe par implémentation (abréviation : m=method, a=authority, s=scheme, p=path) : | Client | Ordre pseudo-headers | |--------|---------------------| | Chrome 119+ | m a s p | | Firefox 90+ | m p a s | | Safari 15+ | m s p a | | curl | m p s a | | Python httpx | m p s a (comme curl) | | Go net/http | m s a p | **Frames PRIORITY** : Firefox 90+ envoie des frames PRIORITY de démarrage pour les streams 3, 5, 7, 9, 11, 13 avant d'initier la première vraie requête. Chrome ≥ 119 n'envoie plus de frames PRIORITY (migration vers [RFC 9218](https://www.rfc-editor.org/rfc/rfc9218) Extended PRIORITY_UPDATE). Safari n'envoie pas de frames PRIORITY. **Format fingerprint composite** (format Akamai) : ``` SETTINGS[HEADER_TABLE_SIZE,ENABLE_PUSH,MAX_CONCURRENT_STREAMS,INITIAL_WINDOW_SIZE,MAX_FRAME_SIZE,MAX_HEADER_LIST_SIZE]|WINDOW_UPDATE[increment]|PRIORITY[stream:exclusive:dep:weight...]|PSEUDO_HEADER_ORDER[m,a,s,p] ``` Exemple Chrome 119 : ``` 1:65536,2:0,3:1000,4:6291456,5:16384,6:262144|WU:15663105|noPRIORITY|masp ``` **Cas d'étude httpcloak** : httpcloak est un outil d'évasion qui tente d'imiter l'empreinte TLS de Chrome. Il réussit à reproduire correctement le JA4 Chrome (SETTINGS H2 et WINDOW_UPDATE), mais son ordre de pseudo-headers par défaut correspond à l'ordre curl (`mpsa`) plutôt qu'à l'ordre Chrome (`masp`). Cette incohérence entre la couche L5 (TLS : empreinte Chrome) et la couche L7 (HTTP/2 : ordre curl) crée un signal de détection à haute précision : score de browser_matcher ≈ 0.60, plaçant la session dans la zone grise et déclenchant l'analyse EIF + AE. --- ### 2.6 Synthèse des limites de l'état de l'art | Technique | Limite principale | Mitigation dans cette architecture | |-----------|-------------------|--------------------------------------| | Règles statiques CRS | Aveugle au trafic syntaxiquement correct | Couche comportementale ML (F1–F8) | | Listes IP/ASN | Botnets résidentiels échappent aux blacklists | ASN = feature parmi d'autres, pas bloquant seul | | JA3 | Instabilité GREASE, collisions inter-versions | JA4 (GREASE filtré, trié), ratio JA3/JA4 comme feature | | JS challenges | Contournés par BotBrowser/CloakBrowser | Architecture 100 % passive, HTTP/2 fingerprinting | | UA matching | Trivial à usurper | UA est une parmi 96 features, cohérence cross-layer | | ML supervisé seul | Concept drift, label scarcity | Semi-supervisé EIF + AE + retraining adaptatif | | Isolation Forest std | Ghost clusters en haute dimension | Extended Isolation Forest (hyperplans aléatoires) | | AE seul | Ne détecte pas les anomalies ponctuelles | Ensemble EIF + AE complémentaires | | Fingerprinting TLS seul | Imitable par httpcloak, CloakBrowser | Corrélation L5 TLS + L7 HTTP/2 (tls_h2_family_mismatch) | | HTTP/2 fingerprinting seul | HTTP/1.1 fallback ne produit pas ce signal | Dégradation gracieuse : features H2 à 0.5 si HTTP/1.1 | | Détection par session | Flottes distribuées → 1 IP semble normale | Graphes bipartis NetworkX (Part B) | | Seuil fixe de classification | Sur-blocage en période calme, sous-blocage en pic | Seuil adaptatif percentile_5 des scores négatifs | ---