Files
ja4-platform/docs/thesis/05_features.md
Jacquin Antoine 0e5f94dd0d docs: restructure thesis into chapter files with corrected references
Split monolithic thesis into separate chapter markdown files under
docs/thesis/. Remove fabricated bibliography entries, correct inflated
claims, add GNN/Transformers section, and rename MetaLearner to Fusion LR.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-13 13:51:38 +02:00

689 lines
31 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

[<< Sommaire](README.md) | [Suivant >>](06_techniques_avancees.md)
---
## 4. Taxonomie des features de détection
Les 96 features sont organisées en 8 familles couvrant les couches L3→L7. Chaque feature est définie par sa formule de calcul, son signal discriminant, ses plages de valeurs typiques (humain vs. bot), et son statut d'implémentation.
**Légende des statuts** :
- `[impl.]` Implémenté et validé en production
- `[partiel]` Partiellement implémenté
- `[todo]` Non encore implémenté
---
### Famille 1 : Volumétrie et vitesse (4 features)
La famille 1 capture les signaux de volume et de cadence bruts. Ces features sont les plus simples mais restent efficaces contre les bots non sophistiqués et constituent une dimension importante du vecteur ML.
#### hits `[impl.]`
**Calcul** : `COUNT(requêtes HTTP) par session dans la fenêtre de 300 s`
**Signal** : un volume de requêtes anormalement élevé en un court laps de temps est le signal de bot le plus basique. Les navigateurs humains sont limités par la vitesse de lecture et d'interaction.
| Type de client | Plage typique |
|---------------|---------------|
| Navigation humaine | 10200 requêtes/5min |
| Navigateur avec auto-refresh agressif | 200500 |
| Bot crawler lent | 2001000 |
| Bot scanner / scraper | 100050 000 |
| DDoS HTTP flood | > 100 000 |
**Note** : hits seul n'est pas suffisant (un humain téléchargeant une page complexe peut générer 200+ requêtes pour les ressources). La combinaison hits × asset_ratio × hit_velocity est nécessaire.
#### hit_velocity `[impl.]`
**Calcul** : `hits / session_duration_seconds`
**Signal** : la vitesse de requêtes par seconde distingue les bots des humains indépendamment de la durée de session. Un humain lisant une page met plusieurs secondes entre deux clics.
| Type de client | Plage typique |
|---------------|---------------|
| Lecture humaine | 0.12 req/s |
| Navigation rapide humaine | 25 req/s |
| Bot crawler lent (poli) | 110 req/s |
| Bot scraper agressif | 10100 req/s |
| DDoS / flood | > 100 req/s |
#### max_keepalives `[impl.]`
**Calcul** : `MAX(requêtes HTTP par connexion TCP) sur la session`
**Signal** : les navigateurs humains utilisent HTTP Keep-Alive de manière modérée (550 requêtes par connexion). Les bots mal configurés ouvrent une nouvelle connexion TCP par requête (max_keepalives = 1, ce qui est exceptionnel pour un navigateur). Certains bots pipeline au contraire entassent des milliers de requêtes dans une seule connexion.
| Type de client | Plage typique |
|---------------|---------------|
| Navigateur humain moderne | 1050 par connexion |
| Scraper simple (connexion par req) | 1 |
| Bot pipeline agressif | > 100 |
**Implémentation** : ja4ebpf incrémente un compteur via le gestionnaire de corrélation in-memory par connexion TCP (`src_ip:src_port`) à chaque requête HTTP reçue. Le maximum sur la session est agrégé dans `view_ai_features_1h`.
#### count_login_post `[impl.]`
**Calcul** : `COUNT(requêtes POST vers /login, /signin, /auth, /wp-login.php, /account/login, /session/new)`
**Signal** : compteur direct des tentatives d'authentification. Un utilisateur humain se connecte 02 fois par session. Un outil de credential stuffing teste des centaines ou milliers de couples identifiant/mot de passe.
| Type de client | Plage typique |
|---------------|---------------|
| Utilisateur humain | 02 |
| Test fonctionnel automatisé | 520 |
| Credential stuffing | > 100 |
---
### Famille 2 : Diversité et exploration (7 features)
La famille 2 capture la diversité des patterns d'accès, distinguant les comportements systématiques des comportements organiques.
#### fuzzing_index `[impl.]`
**Calcul** : `COUNT(DISTINCT query_params) / COUNT(DISTINCT paths)`
**Signal** : un fuzzer d'API teste de nombreuses valeurs de paramètres sur un ensemble restreint d'endpoints (ratio élevé). La navigation normale génère environ autant de chemins distincts que de combinaisons de paramètres distinctes (ratio ≈ 1).
| Type de client | Plage typique |
|---------------|---------------|
| Navigation normale | 0.52 |
| Crawler d'exploration | 15 |
| Fuzzer d'API ciblé | 10100 |
| Scanner de vulnérabilités | > 50 |
#### path_diversity_ratio `[impl.]`
**Calcul** : `COUNT(DISTINCT paths) / hits`
**Signal** : un crawler systématique accède à une URL unique à chaque requête (ratio ≈ 1). Un bot répétitif frappe toujours le même endpoint (ratio ≈ 0.01). La navigation humaine est intermédiaire.
| Type de client | Plage typique |
|---------------|---------------|
| Navigation humaine | 0.30.8 |
| Crawler exhaustif | ≈ 1.0 |
| Bot ciblé (même URL) | < 0.05 |
| Attaque flood sur un endpoint | 0.001 |
#### url_depth_variance `[impl.]`
**Calcul** : `VAR(COUNT('/' in path) per request)`
**Signal** : un crawler stratifié (par profondeur) maintient une profondeur d'URL uniforme dans chaque vague d'exploration, produisant une faible variance. La navigation humaine combine des accès superficiels (homepage, catégories) et profonds (pages produit, articles), produisant une variance naturellement élevée.
#### distinct_ja4_count `[impl.]`
**Calcul** : `COUNT(DISTINCT ja4_fingerprint) par session`
**Signal** : un humain utilisant un seul navigateur produit exactement 1 JA4 distinct. Une flotte de bots utilisant différentes stacks TLS ou un bot qui randomise sa configuration produit plusieurs JA4 distincts dans la même session.
| Type de client | Plage typique |
|---------------|---------------|
| Un seul navigateur | 1 |
| Navigateur avec extension VPN (TLS différent) | 2 |
| Flotte bot multi-outils | 520 |
| Bot à randomisation intensive | > 10 |
#### distinct_header_orders `[impl.]`
**Calcul** : `COUNT(DISTINCT header_order_signature) par session`
**Signal** : chaque bibliothèque HTTP client envoie ses en-têtes dans un ordre fixe. Un navigateur humain produit toujours la même signature d'ordre. Un bot qui alterne entre plusieurs bibliothèques (requests, httpx, aiohttp) révèle cette rotation par la variabilité de l'ordre des en-têtes.
#### is_ua_rotating `[impl.]`
**Calcul** : `1 si COUNT(DISTINCT User-Agent) > 1 dans la session, sinon 0`
**Signal** : un navigateur humain a toujours le même User-Agent. Tout changement de User-Agent dans une session est un signal de rotation artificielle. Feature binaire haute précision.
#### ja4_drift_ratio `[impl.]` (§5.5)
*Voir définition complète dans la Famille 8 (Features comportementales avancées). Comptabilisée ici pour des raisons historiques d'agrégation.*
---
### Famille 3 : Authenticité protocolaire (12 features)
La famille 3 évalue la conformité du client aux standards des navigateurs modernes à travers les en-têtes HTTP et les signaux d'authenticité.
#### modern_browser_score `[impl.]`
**Calcul** : score composite sur la présence des marqueurs caractéristiques des navigateurs modernes : HTTPS, HTTP/2, Brotli, Fetch Metadata headers, Client Hints, Accept-Language, Cookie.
**Plage** : [0, 1] — 0 = aucun marqueur moderne, 1 = tous les marqueurs présents et cohérents.
#### ua_ch_mismatch `[impl.]`
**Calcul** : `1 si (Sec-CH-UA-Mobile: ?1 ET User-Agent contient "Windows"/"Linux"/"Macintosh") OU (User-Agent Chrome récent ET Sec-CH-UA absent)`
**Signal** : incohérence entre les Client Hints (réalité) et le User-Agent (déclaration).
#### has_accept_language `[impl.]`
**Calcul** : `1 si l'en-tête Accept-Language est présent dans la session (ratio > 0.8)`
**Signal** : en-tête toujours présent pour les navigateurs humains.
#### has_cookie `[impl.]`
**Calcul** : `1 si l'en-tête Cookie est présent dans au moins une requête de la session`
**Signal** : un navigateur humain maintient les cookies de session, les tokens CSRF, etc. Un bot stateless n'a aucun cookie.
#### has_referer `[impl.]`
**Calcul** : `ratio de requêtes avec Referer présent dans la session`
**Signal** : les humains naviguent en suivant des liens (Referer présent). Les bots accèdent directement aux URLs.
#### sec_fetch_absence_rate `[impl.]`
**Calcul** : `COUNT(requêtes sans aucun Sec-Fetch-*) / hits`
**Signal** : les Fetch Metadata headers (Sec-Fetch-Site, Mode, Dest) sont présents dans 100 % des requêtes des navigateurs Chrome/Firefox depuis 20192021. Leur absence sur une requête qui prétend provenir d'un navigateur moderne est un signal fort.
#### generic_accept_ratio `[impl.]`
**Calcul** : `COUNT(requêtes avec Accept: */*) / hits`
**Signal** : les navigateurs envoient des en-têtes Accept spécifiques selon le type de ressource (`text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8` pour les documents). Un Accept générique `*/*` est caractéristique des bibliothèques HTTP programmatiques.
#### missing_accept_enc_ratio `[impl.]`
**Calcul** : `COUNT(requêtes sans Accept-Encoding: br) / hits`
**Signal** : l'absence de Brotli dans Accept-Encoding signale un client pré-2016 ou non-navigateur.
#### header_count `[impl.]`
**Calcul** : `AVG(nombre d'en-têtes par requête) sur la session`
**Signal** : les navigateurs modernes envoient en moyenne 1525 en-têtes par requête. Les bibliothèques HTTP minimalistes en envoient 37.
| Type de client | Plage typique |
|---------------|---------------|
| Chrome 119+ | 1825 |
| Firefox 120+ | 1622 |
| Python requests | 47 |
| curl | 35 |
| Scanner réseau | 13 |
#### header_order_confidence `[impl.]`
**Calcul** : score de similarité de l'ordre des en-têtes de la session avec les profils connus (Chrome, Firefox, Safari).
**Plage** : [0, 1] — 1 = correspondance exacte avec un profil connu.
#### sec_ch_mobile_mismatch `[impl.]`
**Calcul** : `1 si Sec-CH-UA-Platform indique "Android"/"iOS" mais User-Agent ou MSS TCP indique desktop`
**Signal** : incohérence cross-layer entre le signal réseau (TCP MSS caractéristique du carrier mobile) et le signal applicatif (CH-UA-Platform).
#### is_fake_navigation `[impl.]`
**Calcul** : feature composite — `1 si Sec-Fetch-Dest: document ET asset_ratio < 0.05 ET has_referer < 0.1`
**Signal** : session qui prétend naviguer (Sec-Fetch présent) mais qui ne charge aucune ressource (pas d'assets) et n'a aucun Referer — comportement impossible pour un vrai navigateur.
---
### Famille 4 : Cohérence cross-layer (14 features)
La famille 4 est le cœur de l'approche multi-couches : elle détecte les incohérences entre les signaux TCP (L4), TLS (L5) et HTTP (L7) qui trahissent les outils d'imitation partielle.
#### alpn_http_mismatch `[impl.]`
**Calcul** : `1 si ALPN négocié = h2 mais requêtes HTTP reçues en HTTP/1.1, OU ALPN = http/1.1 mais connexion HTTP/2`
**Signal** : le protocole applicatif réel ne correspond pas au protocole annoncé dans la négociation TLS.
#### is_alpn_missing `[impl.]`
**Calcul** : `1 si l'extension ALPN est absente du ClientHello TLS`
**Signal** : absence d'ALPN = client non-navigateur ou bibliothèque TLS minimale.
#### sni_host_mismatch `[impl.]`
**Calcul** : `1 si SNI TLS ≠ en-tête HTTP Host`
**Signal** : domain fronting ou proxy mal configuré.
#### mss_mobile_mismatch `[impl.]`
**Calcul** : `1 si MSS TCP ∈ {1340, 1392, 1350} (MSS carrier mobile) ET User-Agent Desktop`
**Signal** : trafic d'un appareil mobile routé via un bot se déclarant desktop.
#### tls12_ratio `[impl.]`
**Calcul** : `COUNT(connexions TLS 1.2) / COUNT(connexions TLS totales)`
**Signal** : Chrome et Firefox utilisent TLS 1.3 exclusivement depuis 2022 pour les connexions modernes. Un ratio TLS 1.2 élevé indique des bibliothèques ou configurations obsolètes.
#### http10_ratio `[impl.]`
**Calcul** : `COUNT(requêtes HTTP/1.0) / hits`
**Signal** : HTTP/1.0 est obsolète depuis HTTP/1.1 (1997). Sa présence signale un outil très ancien ou mal configuré.
#### tcp_jitter_variance `[impl.]`
**Calcul** : `VAR(delta_time entre connexions TCP successives)` (voir §3.3)
#### syn_timing_cv `[impl.]`
**Calcul** : `STDDEV(delay_SYN_to_ClientHello) / MEAN(delay_SYN_to_ClientHello)` (voir §3.3)
#### fingerprint_coherence_score `[impl.]`
**Calcul** : `COUNT(sessions avec JA4 dominant) / COUNT(sessions totales)` — ratio de sessions partageant le même JA4 dominant dans une plage de 5 minutes.
#### h2_settings_known `[impl.]`
**Calcul** : `1 si le vecteur SETTINGS HTTP/2 correspond à un navigateur connu (Chrome/Firefox/Safari/curl/Go)`
**Signal** : SETTINGS inconnu = implémentation HTTP/2 maison ou non répertoriée.
#### h2_pseudo_order_match `[impl.]`
**Calcul** : `1 si l'ordre des pseudo-headers (:method, :authority, :scheme, :path) correspond au navigateur déclaré dans le JA4`
**Signal** : incohérence = outil qui imite le JA4 TLS mais utilise une autre pile HTTP/2.
#### h2_ja4_coherence `[impl.]`
**Calcul** : score de cohérence entre la famille HTTP/2 (SETTINGS + WINDOW_UPDATE + pseudo-header order) et la famille JA4 (fingerprint TLS). Score élevé = même famille technologique pour les deux couches.
#### h2_settings_rare `[impl.]`
**Calcul** : `1 si le vecteur SETTINGS HTTP/2 n'est présent dans aucune base de fingerprints connue`
**Signal** : implémentation HTTP/2 non répertoriée, potentiellement un outil d'évasion personnalisé.
#### tls_h2_family_mismatch `[impl.]`
**Calcul** : feature composite — `1 si JA4 ∈ famille Chrome ET h2_pseudo_order ≠ masp (Chrome order), OU JA4 ∈ famille Firefox ET h2_window_update ≠ plage Firefox`
**Signal** : cette feature est le signal de haute précision central de la famille F4. Elle détecte les outils (httpcloak, BotBrowser partiel) qui imitent correctement la couche TLS mais ne reproduisent pas fidèlement la couche HTTP/2. Un navigateur Chrome légitime présente systématiquement JA4 Chrome + SETTINGS Chrome + WINDOW_UPDATE Chrome + pseudo-order `masp`. Toute discordance expose l'imitation.
---
### Famille 5 : Empreinte réseau (13 features)
La famille 5 agrège les signaux bruts de la couche réseau (L3/L4) qui complètent les signaux applicatifs.
#### ip_id_zero_ratio `[impl.]`
**Calcul** : `COUNT(paquets avec IP ID = 0) / total paquets`
**Signal** : scanners (Masscan, ZMap) génèrent des paquets avec IP ID = 0 systématiquement.
#### request_size_variance `[impl.]`
**Calcul** : `VAR(taille des requêtes HTTP en octets) par session`
**Signal** : les requêtes humaines varient en taille (saisie de formulaires, paramètres de recherche différents). Les bots automatisés envoient souvent des requêtes de taille identique ou très régulière.
#### anomalous_payload_ratio `[impl.]`
**Calcul** : `COUNT(requêtes avec payloads > 3σ de la moyenne de la session) / hits`
**Signal** : injection de charges utiles anormalement grandes (tentatives de buffer overflow, XXE, SQLi dans le body).
#### avg_ttl `[impl.]`
**Calcul** : `AVG(IP TTL observé) sur la session`
**Signal** : fingerprint OS résiduel (voir §3.2).
#### ttl_std `[impl.]`
**Calcul** : `STDDEV(IP TTL) sur la session`
**Signal** : un TTL constant indique un seul OS ou une manipulation. Une variance élevée indique des connexions depuis différents OS ou routes.
#### no_window_scale_ratio `[impl.]`
**Calcul** : `COUNT(connexions SYN sans option Window Scale) / total connexions`
**Signal** : absence de Window Scale = stack TCP minimal, OS obsolète, ou outil de scan.
#### ip_df_variance `[impl.]`
**Calcul** : `VAR(IP DF bit) sur la session`
**Signal** : instabilité du bit DF = stack réseau non standard ou tunnel changeant.
#### tcp_shared_count `[impl.]`
**Calcul** : `AVG(max_keepalives) sur la session`
**Signal** : voir §3.3.
#### port_exhaustion_ratio `[impl.]`
**Calcul** : `COUNT(connexions avec src_port > 60000) / total connexions`
**Signal** : un hôte émettant un très grand nombre de connexions rapides épuise sa plage de ports (16-bit, max 65535). Un ratio élevé de ports > 60000 indique une source à haute fréquence de connexions.
#### src_port_density `[impl.]`
**Calcul** : `(max_src_port - min_src_port) / total connexions`
**Signal** : un bot envoyant des connexions rapides depuis un seul processus utilise des ports séquentiels (densité faible). Un humain réel ouvre peu de connexions avec une plage de ports plus dispersée.
#### has_xff `[impl.]`
**Calcul** : `1 si l'en-tête X-Forwarded-For est présent dans au moins une requête de la session`
**Signal** : trafic via CDN ou proxy. Conséquence directe sur le pipeline : neutralisation des dimensions H2 du browser_matcher (voir §3.6).
#### true_window_size `[impl.]`
**Calcul** : `tcp_meta_window_size × 2^tcp_meta_window_scale`
**Signal** : taille effective de la fenêtre TCP après application du facteur d'échelle. Fingerprint OS/stack TCP.
#### window_mss_ratio `[impl.]`
**Calcul** : `true_window_size / tcp_meta_mss`
**Signal** : ratio diagnostique. Pour Linux standard : 64240 / 1460 ≈ 44. Des valeurs très différentes signalent des configurations non standard ou des manipulations.
---
### Famille 6 : Comportement de navigation (10 features)
La famille 6 capture le comportement de navigation macroscopique sur la fenêtre de 300 secondes.
#### asset_ratio `[impl.]`
**Calcul** : `COUNT(requêtes vers CSS/JS/images/fonts) / hits`
**Signal** : voir §2.3.2.
#### direct_access_ratio `[impl.]`
**Calcul** : `COUNT(requêtes sans Referer) / hits`
**Signal** : voir §2.3.2.
#### orphan_ratio `[impl.]`
**Calcul** : `COUNT(requêtes HTTP sans données L3/L4 associées) / hits`
**Signal** : ratio élevé = trafic majoritairement via proxy/CDN cassant la corrélation L4↔L7. Peut masquer des signaux TLS/TCP. Feature informative pour la fusion LR.
#### temporal_entropy `[impl.]`
**Calcul** : entropie de Shannon sur la distribution des requêtes par intervalle de temps de 30 s dans la fenêtre de 300 s : `H = -Σ pi × log2(pi)`
**Signal** : distribution uniforme (entropie maximale) = navigation naturelle. Distribution très concentrée (entropie faible) = burst automatisé.
#### post_ratio `[impl.]`
**Calcul** : `COUNT(requêtes POST) / hits`
**Signal** : voir §2.3.3.
#### head_ratio `[impl.]`
**Calcul** : `COUNT(requêtes HEAD) / hits`
**Signal** : les requêtes HEAD (sans corps de réponse) sont utilisées par des outils de check de disponibilité, des crawlers de métadonnées, ou des outils de reconnaissance. Rares dans la navigation humaine.
#### http_scheme_ratio `[impl.]`
**Calcul** : `COUNT(requêtes HTTP non-HTTPS) / hits`
**Signal** : les navigateurs modernes utilisent HTTPS exclusivement depuis 2018 environ. Des requêtes HTTP en clair signalent des outils anciens ou mal configurés.
#### login_post_concentration `[impl.]`
**Calcul** : `count_login_post / COUNT(requêtes POST totales)`
**Signal** : voir §2.3.3 — mesure si les POSTs se concentrent sur les endpoints d'authentification.
#### unusual_content_type_ratio `[impl.]`
**Calcul** : `COUNT(requêtes avec Content-Type absent ou non-standard) / hits`
**Signal** : les navigateurs envoient des Content-Type précis selon le contexte. Les outils automatisés omettent souvent ce header ou envoient des valeurs génériques.
#### non_standard_port_ratio `[impl.]`
**Calcul** : `COUNT(requêtes vers ports non-standards : pas 80/443/8080/8443) / hits`
**Signal** : les navigateurs humains n'accèdent que très rarement à des ports non-standards. Les bots ciblant des services d'administration ou des services exposés accèdent à des ports inhabituels.
---
### Famille 7 : Intelligence contextuelle (23 features)
La famille 7 combine la connaissance des bases de référence (JA4 connu, ASN, profils de navigateurs) avec les scores du browser_matcher.
#### ja4_asn_concentration `[impl.]`
**Calcul** : `COUNT(sessions avec même JA4 ET même ASN) / COUNT(sessions avec même JA4)`
**Signal** : une flotte de bots utilisant le même outil depuis le même datacenter produit une concentration JA4/ASN élevée, révélant une campagne coordonnée.
#### ja4_country_concentration `[impl.]`
**Calcul** : `entropy(distribution pays) pour les sessions partageant un JA4` — faible entropie = origines géographiques concentrées.
#### is_rare_ja4 `[impl.]`
**Calcul** : `1 si le JA4 de la session est absent de la base de fingerprints JA4 connus`
**Signal** : implémentation TLS non répertoriée — peut indiquer un nouvel outil d'évasion.
#### header_order_shared_count `[impl.]`
**Calcul** : `COUNT(sessions partageant exactement la même header_order_signature)` dans la fenêtre de 300 s.
**Signal** : de nombreuses sessions avec la même signature d'ordre d'en-têtes indique une flotte utilisant la même bibliothèque HTTP sans randomisation.
#### ja3_diversity_ratio `[impl.]`
**Calcul** : voir §2.2.3.
#### anubis_is_flagged `[impl.]`
**Calcul** : `1 si la session a une correspondance Anubis WEIGH ou DENY`
**Signal** : signal auxiliaire de la base Anubis, sans action bloquante dans le vecteur ML.
#### multiplexing_efficiency `[impl.]`
**Calcul** : `COUNT(streams HTTP/2 uniques) / COUNT(connexions TCP totales)`
**Signal** : un navigateur humain utilise efficacement le multiplexage HTTP/2 (plusieurs streams par connexion). Un bot ouvrant une connexion par requête a une efficacité = 1.
#### browser_confidence `[impl.]`
**Calcul** : score de confiance global du browser_matcher (voir §3.8).
#### browser_family `[impl.]`
**Calcul** : famille détectée : `chrome | firefox | safari | curl | python | go | unknown`
**Signal** : permet la décomposition des features par famille de clients.
#### is_known_browser `[impl.]`
**Calcul** : `1 si browser_confidence ≥ 0.70`
**Signal** : client correspondant à un profil de navigateur connu avec haute confiance.
#### browser_consistency_score `[impl.]`
**Calcul** : cohérence entre les features L5, L7, et H2 au regard du navigateur déclaré dans le User-Agent.
#### axis_ja4_known `[impl.]`
**Calcul** : dimension JA4 du browser_matcher — `1 si JA4 figure dans la base Chrome/Firefox/Safari connue`
#### axis_ja4_struct `[impl.]`
**Calcul** : dimension structurelle JA4 — score de conformité de la structure JA4 (nombre de ciphers, d'extensions, ALPN, SNI) aux navigateurs connus.
#### axis_http_modern `[impl.]`
**Calcul** : dimension modernité HTTP — composite de `sec_fetch_absence_rate`, `has_accept_language`, `modern_browser_score`, `ua_ch_mismatch`.
#### axis_nav_behavior `[impl.]`
**Calcul** : dimension comportement de navigation — composite de `asset_ratio`, `direct_access_ratio`, `orphan_ratio`, `post_ratio`.
#### axis_tls_coherence `[impl.]`
**Calcul** : dimension cohérence TLS — composite de `tls12_ratio`, `is_alpn_missing`, `sni_host_mismatch`, `fingerprint_coherence_score`.
#### axis_h2_coherence `[impl.]`
**Calcul** : dimension cohérence HTTP/2 — composite de `h2_settings_known`, `h2_pseudo_order_match`, `h2_ja4_coherence`.
#### browser_match_chrome `[impl.]`
**Calcul** : score de correspondance spécifique au profil Chrome (SETTINGS H2 + WINDOW_UPDATE + pseudo-order masp + JA4 Chrome).
#### browser_match_firefox `[impl.]`
**Calcul** : score de correspondance spécifique au profil Firefox.
#### browser_match_safari `[impl.]`
**Calcul** : score de correspondance spécifique au profil Safari.
#### browser_match_max `[impl.]`
**Calcul** : `MAX(browser_match_chrome, browser_match_firefox, browser_match_safari)`
**Signal** : score de correspondance au meilleur navigateur candidat.
#### h2_window_update_value `[impl.]`
**Calcul** : valeur brute de l'incrément WINDOW_UPDATE HTTP/2 (voir §2.5.3).
**Signal** : valeur distinctive par implémentation — voir table §2.5.3.
#### h2_has_priority_frames `[impl.]`
**Calcul** : `1 si des frames PRIORITY HTTP/2 sont reçues avant la première requête`
**Signal** : Firefox envoie des PRIORITY frames de démarrage. Chrome ≥ 119 n'en envoie plus. Absence ≠ non-Firefox ; présence = signal Firefox fort.
---
### Famille 8 : Features comportementales avancées (13 features)
La famille 8 regroupe les features originales développées spécifiquement pour cette architecture, capturant des signaux comportementaux avancés non présents dans la littérature standard.
#### path_transition_entropy `[impl.]`
**Calcul** : entropie de Shannon sur la matrice de transition entre chemins successifs. Pour chaque paire de chemins consécutifs (path_i → path_{i+1}), construire la distribution de probabilité des transitions ; calculer H = -Σ p(a→b) × log2(p(a→b)).
**Signal** : navigation humaine = entropie élevée (imprévisible). Crawler systématique = entropie faible (suivi d'un pattern fixe). Bot répétitif = entropie proche de 0 (même transition répétée).
#### cadence_cv `[impl.]`
**Calcul** : `STDDEV(inter_request_intervals_ms) / MEAN(inter_request_intervals_ms)`
**Signal** : coefficient de variation des intervalles inter-requêtes. Humain : CV ≈ 0.82.0 (variabilité naturelle). Bot à timer fixe : CV ≈ 0.010.05 (régularité mécanique). Bot avec jitter artificiel : CV ≈ 0.10.3 (moins régulier que timer fixe, moins variable qu'humain).
#### lag1_autocorrelation `[impl.]`
**Calcul** : corrélation de Pearson entre la série des intervalles inter-requêtes et la même série décalée de 1 : `corr(I[t], I[t-1])` où I[t] est l'intervalle entre la requête t et la requête t+1.
**Signal** : humain : autocorrélation faible (chaque intervalle est indépendant — dépend de la lecture, de l'interaction, etc.). Bot à burst-pause : autocorrélation positive forte (les longs intervalles suivent les longs intervalles, les courts suivent les courts). Timer avec bruit : autocorrélation proche de 0 mais variance très faible.
#### burst_ratio `[impl.]`
**Calcul** : `COUNT(inter-request intervals < percentile_10(intervals)) / hits`
**Signal** : ratio de requêtes arrivant en rafale (intervalles très courts). Les bots soumettent souvent des lots de requêtes suivis de pauses, alors que les humains ont une distribution plus continue.
#### pause_ratio `[impl.]`
**Calcul** : `COUNT(inter-request intervals > percentile_90(intervals)) / hits`
**Signal** : ratio de longues pauses. Complémentaire de burst_ratio. Un humain a un ratio de pauses naturellement élevé (lecture, réflexion). Un bot avec timer fixe a un ratio de pauses presque nul (timer constant).
#### benford_deviation `[impl.]`
**Calcul** : déviation par rapport à la loi de Benford (loi du premier chiffre significatif) sur les intervalles inter-requêtes en millisecondes.
**Loi de Benford** (aussi appelée loi du premier chiffre ou loi de Newcomb-Benford) : la probabilité que le premier chiffre significatif d'un nombre provenant de données naturelles soit d (d ∈ {1,...,9}) suit :
```
P(d) = log₁₀(1 + 1/d)
```
Soit : P(1) ≈ 30.1 %, P(2) ≈ 17.6 %, P(3) ≈ 12.5 %, ..., P(9) ≈ 4.6 %.
Les séries naturelles (intervalles de navigation humaine, données de population, transactions financières) suivent cette distribution. Les intervalles générés artificiellement (timers à période fixe, sleep() programmé) dévient significativement.
**Calcul de la déviation** :
```
D_benford = Σ_{d=1}^{9} |f_observed(d) - P(d)|
```
où f_observed(d) est la fréquence observée du premier chiffre d dans la série des intervalles inter-requêtes.
**Signal** : humain ≈ 0.050.20, bot à timer fixe > 0.40, bot avec jitter ≈ 0.200.35.
> **Caveat — Applicabilité de la loi de Benford** : l'application de la loi de Benford aux intervalles inter-requêtes est une hypothèse heuristique, non démontrée mathématiquement pour ce cas d'usage. La loi de Benford exige que les données couvrent plusieurs ordres de grandeur de manière approximativement log-normale ; les intervalles inter-requêtes (typiquement quelques ms à quelques secondes) ne couvrent pas nécessairement ces conditions. Les seuils de déviation cités (ex. 0.40) sont empiriques et nécessitent une validation sur les données de production. Cette feature est conservée comme signal auxiliaire, mais ne doit pas être utilisée comme critère de décision principal.
#### root_to_first_asset_delay `[impl.]`
**Calcul** : délai en ms entre la requête vers la page racine (`/` ou chemin HTML) et la première requête d'asset (CSS/JS/image) dans la même session.
**Signal** : un navigateur humain déclenche immédiatement le chargement des assets après avoir reçu le HTML (délai 50200 ms, dépendant du temps de parsing). Un scraper qui charge le HTML sans le parser ne charge jamais les assets (valeur infinie, traitée comme NULL). Un bot qui simule les assets artificiellement peut avoir un délai non-physiologique (valeur théoriquement attendue très faible, nécessitant validation empirique sur l'infrastructure cible, ou > 5 000 ms).
#### asset_load_stddev `[impl.]`
**Calcul** : `STDDEV(délais entre requêtes d'assets successifs)` dans une cascade de chargement.
**Signal** : un navigateur réel charge les assets en parallèle avec des contraintes réseau naturelles, produisant une certaine variance. Un bot simulant des assets de manière séquentielle ou avec un délai fixe produit une variance anormalement faible.
#### ja4_drift_ratio `[impl.]`
**Calcul** : `COUNT(transitions du JA4 dominant entre segments temporels) / (nb_segments - 1)`
La session est divisée en segments de N requêtes. Le JA4 dominant de chaque segment est calculé (mode). `ja4_drift_ratio` compte les transitions entre JA4 dominants consécutifs, normalisé par le nombre de transitions possibles. La feature est calculée dans `view_thesis_features_1h` (fenêtres temporelles étendues d'une heure).
**Signal** : un humain avec un seul navigateur a un ratio de 0. Un bot qui change de configuration TLS entre différentes phases d'activité (reconnaissance → exploitation) produit un ratio proche de 1. Voir §5.5 pour la description complète et le contexte de détection des bots APT multi-phases.
**Plages typiques** : humain = 0 ; bot multi-phase = 0,51,0 ; bot TLS aléatoire = proche de 1,0.
#### host_diversity `[impl.]`
**Calcul** : `COUNT(DISTINCT en-têtes Host) par session`
**Signal** : un navigateur humain peut accéder à plusieurs domaines (portail, sous-domaines). Un bot de scan horizontal accède à de nombreux hosts distincts depuis la même IP. Feature critique pour la détection de bots qui parcourent plusieurs sites.
#### host_sweep_speed `[impl.]`
**Calcul** : `COUNT(DISTINCT hosts) / session_duration_minutes`
**Signal** : vitesse d'accès à de nouveaux hosts. Un scanner horizontal accède à un nouveau host par seconde ou plus vite. Un navigateur humain accède rarement à plus de 10 domaines distincts par minute.
#### host_coverage_uniformity `[impl.]`
**Calcul** : `1 - (STDDEV(requêtes par host) / MEAN(requêtes par host))`
**Signal** : un crawler systématique accède à chaque host avec le même nombre de requêtes (couverture uniforme). Un humain accède beaucoup plus à certains sites qu'à d'autres.
#### cross_domain_path_similarity `[impl.]`
**Calcul** : similarité de Jaccard entre les ensembles de chemins accédés sur différents hosts : `|paths(host_A) ∩ paths(host_B)| / |paths(host_A) paths(host_B)|`
**Signal** : un scanner accède aux mêmes chemins sur tous les hosts (similarité élevée : `/admin`, `/login`, `/config.php` sur chaque site). Un navigateur humain a des chemins spécifiques à chaque site (similarité faible).