Rewrite fleet.py to use a GNN-based approach: nodes are src_ip with ML feature vectors, edges connect IPs sharing (JA4, ASN) pairs, GraphSAGE (2 SAGEConv layers, in→64→32) produces 32D embeddings clustered by HDBSCAN. PyG NeighborLoader activates for >50k nodes. Update thesis docs (§5.2, §6.4, §2, §8) to reflect GraphSAGE architecture and PyG scalability. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
33 KiB
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 | 10–200 requêtes/5min |
| Navigateur avec auto-refresh agressif | 200–500 |
| Bot crawler lent | 200–1000 |
| Bot scanner / scraper | 1000–50 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.1–2 req/s |
| Navigation rapide humaine | 2–5 req/s |
| Bot crawler lent (poli) | 1–10 req/s |
| Bot scraper agressif | 10–100 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 (5–50 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 | 10–50 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 0–2 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 | 0–2 |
| Test fonctionnel automatisé | 5–20 |
| 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.5–2 |
| Crawler d'exploration | 1–5 |
| Fuzzer d'API ciblé | 10–100 |
| 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.3–0.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 | 5–20 |
| 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 2019–2021. 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 15–25 en-têtes par requête. Les bibliothèques HTTP minimalistes en envoient 3–7.
| Type de client | Plage typique |
|---|---|
| Chrome 119+ | 18–25 |
| Firefox 120+ | 16–22 |
| Python requests | 4–7 |
| curl | 3–5 |
| Scanner réseau | 1–3 |
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.
session_transformer_embedding [impl.] — Vecteur Float32, dimension 32
Calcul : embedding dense produit par un Transformer encoder (2 couches, 4 têtes d'attention, d_model=64) prenant en entrée la séquence ordonnée des requêtes d'une session. Chaque requête est tokenisée en trois signaux : le chemin URL (hashé → embedding), la méthode HTTP (embedding catégoriel) et le délai inter-requête Δt (normalisé log1p(Δt_ms)/10 → projection linéaire). Un mean pooling sur la sortie du Transformer, suivi d'une couche linéaire, produit le vecteur final de dimension 32. Les 32 dimensions sont exposées comme colonnes seq_emb_0 à seq_emb_31.
Ce vecteur remplace les statistiques agrégées basiques (path_transition_entropy, cadence_cv) par une représentation contextuelle capable de capturer les dépendances longues dans la séquence de navigation, au lieu de se limiter à une modélisation Markovienne d'ordre 1 ou à des moments statistiques isolés.
Signal : le Transformer encode conjointement la structure des chemins, les méthodes HTTP et le rythme temporel. Navigation humaine = vecteur structuré (transitions cohérentes, variabilité temporelle naturelle). Crawler systématique = vecteur concentré dans une région distincte de l'espace latent (séquences répétitives, Δt constants). Bot avancé avec randomisation = vecteur identifiable par l'absence de la structure attentionnelle propre à la navigation organique.
Robustesse : session ≤ 1 requête → vecteur nul (32 zéros). Session > 512 requêtes → troncation aux 512 dernières. Poids indisponibles → fallback à zéros avec avertissement journalisé.
cadence_cv [absorbé par session_transformer_embedding]
Note : cette feature est désormais capturée par le Session Transformer (§5.1) via le signal Δt projeté et les couches de self-attention, qui modélisent la variabilité temporelle de manière contextuelle plutôt que par un coefficient de variation scalaire isolé. La colonne SQL subsiste dans view_thesis_features_1h pour compatibilité, mais n'est plus utilisée dans le vecteur feature du modèle.
Ancien calcul : STDDEV(inter_request_intervals_ms) / MEAN(inter_request_intervals_ms)
Ancien signal : coefficient de variation des intervalles inter-requêtes. Humain : CV ≈ 0.8–2.0 (variabilité naturelle). Bot à timer fixe : CV ≈ 0.01–0.05 (régularité mécanique). Bot avec jitter artificiel : CV ≈ 0.1–0.3.
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.05–0.20, bot à timer fixe > 0.40, bot avec jitter ≈ 0.20–0.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 50–200 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,5–1,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).