Files
ja4-platform/docs/services/bot-detector.md
Jacquin Antoine f88b739992 feat(e2e): add distributed E2E test framework with parametric traffic generation
Add run-e2e-test.sh with CLI parameters (--hits, --http-ratio, --dns, --tls,
--src-ips, --keep-analysis, --up) for configurable traffic generation. Traffic
runs from VM endpoints with multiple source IPs (alias IPs on eth0) to produce
distinct sessions for the ML pipeline. Fix curl TLS flags (--tlsv1.2 instead
of --tls-v1-2), skip redundant local verification in distributed mode, and
fix dashboard is_available() cache that never retried after ClickHouse recovery.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-15 00:09:32 +02:00

35 KiB
Raw Blame History

Bot Detector

Service Python de détection d'anomalies par apprentissage automatique semi-supervisé sur le trafic HTTP/TLS agrégé dans ClickHouse. Fonctionne en cycle continu (par défaut toutes les 5 minutes) avec un ensemble à triple voix (Extended Isolation Forest + NFEnsemble + XGBoost) piloté par un méta-learner MLP, enrichi par l'explicabilité ExIFFI et SHAP, le clustering HDBSCAN, la détection de flottes coordonnées (PyTorch Geometric GraphSAGE) et la surveillance de performance par cycle.


Architecture des modules

Le service est découpé en 16 modules organisés ainsi :

__main__.py          Point d'entrée (python -m bot_detector)
  └─ cycle.py        Boucle principale : requête ClickHouse → pipeline → insertion
       ├─ config.py          Variables d'environnement, flags de disponibilité
       ├─ log.py             Journalisation structurée JSON (structlog + RotatingFileHandler)
       ├─ infra.py           Client ClickHouse (via ja4_common), health check HTTP, arrêt propre
       ├─ preprocessing.py   Nettoyage du DataFrame, imputation, listes de features (FEATURES, FEATURES_COMPLET)
       │    └─ browser.py    Identification multifactorielle des navigateurs (6 axes)
       ├─ pipeline.py        Orchestration : filtrage → entraînement → MetaLearner → ExIFFI → scoring → fusion
       │    ├─ models.py     EIF, NFEnsemble/TrafficNormalizingFlow (PyTorch), XGBoost
       │    └─ scoring.py    Normalisation, MetaLearner MLP, seuil adaptatif, ExIFFI, SHAP, HDBSCAN, dérive ADWIN+KS+KL
       ├─ browser_matcher.py         Scoring H2 statique à 7 dimensions pondérées
       │    └─ browser_signatures.py Signatures statiques Chrome/Firefox/Safari + rechargement ClickHouse
       ├─ browser_matcher_dynamic.py Scoring H2 dynamique temps réel (profils auto-appris)
       ├─ profile_builder.py         Profiling HDBSCAN hors-ligne, centroïdes, lifecycle (cron quotidien)
       ├─ fleet.py           Graphe bipartite JA4×ASN (NetworkX), fleet_score, fleet_detections
       ├─ metrics.py         Métriques de cycle, alertes, ml_performance_metrics
       └─ (insère dans ml_all_scores + ml_detected_anomalies + fleet_detections + ml_performance_metrics)
Module Lignes Rôle
config.py 154 Toute la configuration via os.getenv(), flags de disponibilité des librairies
log.py 65 log_info(), log_decision(), append_training_history() — JSONL rotatif
infra.py 89 Client ClickHouse (délègue à ja4_common), score_to_threat_level(), serveur de santé en thread daemon
browser.py 191 Détection multifactorielle des navigateurs sur 6 axes pondérés (ajout axis_h2_coherence)
browser_matcher.py 498 Scoring H2 statique à 7 dimensions pondérées (SETTINGS, WINDOW_UPDATE, pseudo-order, etc.)
browser_signatures.py 166 Signatures statiques Chrome/Firefox/Safari + rechargement dynamique depuis ClickHouse
browser_matcher_dynamic.py 387 Scoring H2 dynamique temps réel contre profils auto-appris (auto_browser_profiles)
profile_builder.py 614 Profiling HDBSCAN hors-ligne : clustering, centroïdes, fusion, lifecycle (cron quotidien)
scoring.py 564 MetaLearner MLP, normalisation, seuil adaptatif, ExIFFI, SHAP top-5, HDBSCAN, dérive ADWIN+KS+KL
models.py 484 NFEnsemble/TrafficNormalizingFlow, entraînement/chargement EIF, XGBoost, élagage de features
preprocessing.py 127 preprocess_df() — nettoyage, typage, imputation, listes FEATURES / FEATURES_COMPLET
pipeline.py 441 run_semi_supervised_logic() — orchestration complète d'un modèle, MetaLearner, ExIFFI
fleet.py 174 build_fleet_graph(), detect_fleet_communities(), enrich_with_fleet_score() — PyTorch Geometric GraphSAGE + HDBSCAN
metrics.py 166 record_cycle_metrics(), _emit_alerts() — table ml_performance_metrics
cycle.py 415 fetch_and_analyze() — boucle principale, feedback SOC, multiwindow
__main__.py 41 Point d'entrée, bannière de démarrage, boucle while True

Configuration

Toute la configuration est lue via os.getenv() dans config.py. Aucun fichier YAML ni pydantic-settings.

Connexion ClickHouse

Variable Type Défaut Description
CLICKHOUSE_HOST str clickhouse Nom d'hôte du serveur ClickHouse (via ja4_common)
CLICKHOUSE_PORT int 8123 Port HTTP ClickHouse (via ja4_common)
CLICKHOUSE_USER str admin Utilisateur ClickHouse (via ja4_common)
CLICKHOUSE_PASSWORD str "" Mot de passe ClickHouse (via ja4_common)
CLICKHOUSE_DB_PROCESSING str ja4_processing Base de données ML/agrégations (fallback : CLICKHOUSE_DB)
CLICKHOUSE_DB_LOGS str ja4_logs Base de données des logs HTTP

Modèle et entraînement

Variable Type Défaut Description
N_ESTIMATORS int 300 Nombre d'arbres pour l'Extended Isolation Forest
ISOLATION_CONTAMINATION float 0.001 Paramètre de contamination EIF (plage ]0, 0.5[)
ANOMALY_THRESHOLD float -0.05 Seuil de score brut pour la détection d'anomalie
ANOMALY_PERCENTILE int 5 Percentile pour le seuil adaptatif
MODEL_DIR str /var/lib/bot_detector Répertoire de persistance des modèles
MODEL_HISTORY_COUNT int 10 Nombre de versions de modèle conservées
RETRAIN_INTERVAL_HOURS int 24 Intervalle de réentraînement EIF (heures)
DRIFT_THRESHOLD float 0.30 Seuil de dérive KS (fraction de features driftées)
MIN_VALID_FEATURE_RATIO float 0.50 Ratio minimal de features valides pour entraîner
PRUNE_VARIANCE_THRESHOLD float 1e-6 Seuil de variance pour l'élagage de features
VAL_ANOMALY_GATE float 0.20 Garde-fou : taux maximum d'anomalies en validation
MIN_HUMAN_BASELINE int 500 Nombre minimum de sessions humaines pour entraîner l'IF
BASELINE_ACCEPT_UNKNOWN bool false Mode test : utiliser ASN unknown comme fallback si baseline ISP insuffisante

NFEnsemble (Deep Ensemble M=5 Normalizing Flows)

Variable Type Défaut Description
AE_WEIGHT float 0.30 Poids du NFEnsemble dans le score combiné (plage ]0, 1[)
AE_EPOCHS int 50 Nombre d'époques d'entraînement par membre
AE_LATENT_DIM int 16 Dimension de l'espace latent (coupling layers RealNVP)
AE_LEARNING_RATE float 1e-3 Taux d'apprentissage Adam
NF_UNCERTAINTY_THRESHOLD float 1.0 Seuil de variance inter-modèles pour la détection adversariale

XGBoost

Variable Type Défaut Description
XGB_WEIGHT float 0.20 Poids de XGBoost dans le score final (plage ]0, 1[)
XGB_MIN_LABELS int 100 Nombre minimal de labels SOC pour activer XGBoost
XGB_RETRAIN_INTERVAL_HOURS int 168 Intervalle de réentraînement XGBoost (7 jours)

Détection navigateur

Variable Type Défaut Description
BROWSER_CONFIDENCE_THRESHOLD float 0.55 Confiance minimale pour classifier LEGITIMATE_BROWSER
BROWSER_COHORT_RATIO float 0.70 Si ≥ 70 % des sessions d'un JA4 sont navigateur → propagation

Clustering et explicabilité

Variable Type Défaut Description
ENABLE_CLUSTERING bool true Activer le clustering HDBSCAN
CLUSTERING_MIN_SAMPLES int 3 Taille minimale de cluster
ENABLE_SHAP bool true Activer l'explicabilité SHAP (requiert shap installé)

Cycle et opérations

Variable Type Défaut Description
CYCLE_INTERVAL_SEC int 300 Intervalle entre les cycles (secondes)
MAX_CONSECUTIVE_FAILURES int 3 Échecs consécutifs avant healthy=False
DEDUP_TTL_MIN int 60 TTL de déduplication inter-cycle (minutes)
RECURRENCE_WEIGHT float 0.005 Poids de la récurrence dans le score brut
ENABLE_MULTIWINDOW bool false Activer l'analyse multi-fenêtre 24h
MULTIWINDOW_VIEW str view_ai_features_24h Vue ClickHouse pour le mode multi-fenêtre
ENABLE_FEEDBACK bool true Activer l'intégration du feedback SOC
FEEDBACK_WINDOW_DAYS int 7 Fenêtre de feedback SOC (jours)

Journalisation et santé

Variable Type Défaut Description
BOT_DETECTOR_LOG str /var/log/bot_detector/decisions.jsonl Chemin du fichier de décisions
LOG_BACKUP_COUNT int 7 Nombre de fichiers de rotation conservés
HEALTH_PORT int 8080 Port du serveur de santé HTTP

Pipeline ML — Ensemble à triple voix

Vue d'ensemble

Le bot-detector utilise trois modèles en parallèle, combinés par une pondération configurable :

                  ┌──────────────────────┐
                  │  Extended Isolation   │
                  │  Forest (isotree)     │──→ eif_norm (01)
                  └──────────────────────┘        │
                                                  │ × (1  AE_WEIGHT)
                  ┌──────────────────────┐        │
                  │  NFEnsemble (M=5 NF)  │        ├──→ combined_norm
                  │  (PyTorch RealNVP)    │──→ ae_norm  (01)
                  └──────────────────────┘   × AE_WEIGHT
                                                  │ × (1  XGB_WEIGHT)
                  ┌──────────────────────┐        │
                  │  XGBoost             │        ├──→ anomaly_score
                  │  (supervisé, labels  │──→ xgb_prob  (01)
                  │   SOC + Cleanlab)    │   × XGB_WEIGHT
                  └──────────────────────┘

Formule du score final :

combined_norm  = (1  AE_WEIGHT) × eif_norm + AE_WEIGHT × ae_norm
anomaly_score  = (1  XGB_WEIGHT) × combined_norm + XGB_WEIGHT × xgb_prob

Avec les poids par défaut (AE_WEIGHT=0.30, XGB_WEIGHT=0.20) :

anomaly_score = 0.56 × eif_norm + 0.24 × ae_norm + 0.20 × xgb_prob

Architecture duale

Deux modèles indépendants tournent sur chaque cycle :

Modèle Condition Features Données
Complet correlated = 1 85 (FEATURES_COMPLET) HTTP + TCP + TLS (L3→L7)
Applicatif correlated = 0 73 (FEATURES) HTTP seul (L7 pur)

En mode multi-fenêtre (ENABLE_MULTIWINDOW=true), deux variantes supplémentaires sont exécutées sur la vue 24h : Complet_24h et Applicatif_24h.

Extended Isolation Forest (EIF)

Modèle principal non supervisé. Utilise isotree.IsolationForest :

isotree.IsolationForest(
    ntrees=300,              # N_ESTIMATORS
    ndim=min(3, n_features),
    sample_size='auto',
    missing_action='impute',
    random_seed=42,
    nthreads=-1,
)

Fallback si isotree n'est pas disponible : sklearn.ensemble.IsolationForest(n_estimators=300, contamination=CONTAMINATION).

Calibration : le score isotree brut (∈ [0, 1], >0.5 = anomalous) est converti en convention sklearn : sklearn_equiv = 0.5 isotree_score.

NFEnsemble — Deep Ensemble M=5 Normalizing Flows (PyTorch)

Le NFEnsemble est un deep ensemble de M=5 TrafficNormalizingFlow indépendants, chacun basé sur l'architecture RealNVP (Dinh et al., 2017) avec couches de coupling affines, permutations fixes et batch normalization.

TrafficNormalizingFlow (membre individuel) :

Couche 1 : Permutation fixe → RealNVP coupling → BatchNorm
Couche 2 : Permutation fixe → RealNVP coupling → BatchNorm
...
Sortie : log-probabilité exacte par la règle de changement de variable
  • Coupling layers : transformations affines conditionnées (scale + shift)
  • Permutations : permutations fixes alternées entre les couches
  • BatchNorm : normalisation par batch entre les couches de coupling
  • Optimiseur : Adam(lr=1e-3, weight_decay=1e-5)
  • Perte : Negative Log-Likelihood (NLL) — -log p(x) via la règle de changement de variable
  • Entraînement : 50 époques, batch_size=256, échantillonnage bootstrap (avec remise) par membre
  • Score : -log p(x) — plus élevé = plus anomalous
  • Normalisation des entrées : min-max [0, 1] par feature

NFEnsemble (M=5, Lakshminarayanan et al., 2017) :

  • Incertitude : la variance inter-modèles des scores -log p(x) quantifie l'incertitude épistémique.
    • Faible incertitude → dérive organique (les modèles s'accordent)
    • Forte incertitude → dérive adversariale probable (les modèles divergent)
  • Seuil d'incertitude : NF_UNCERTAINTY_THRESHOLD (défaut 1.0) — au-delà, is_adversarial_drift = True
  • Score final : moyenne des -log p(x) sur les M modèles (rétro-compatible avec le pipeline)
  • Sérialisation : state_dict() contient ensemble_size, n_features, et les state_dict de chaque membre

XGBoost (supervisé)

Entraîné sur les labels issus du feedback SOC (table soc_feedback), filtrés par Cleanlab :

xgb.XGBClassifier(
    n_estimators=200,
    max_depth=6,
    learning_rate=0.1,
    scale_pos_weight=auto,  # max(1, n_neg / n_pos)
    eval_metric='logloss',
    tree_method='hist',
    random_state=42,
    n_jobs=-1,
)
  • Labels positifs (bot) : HIGH, CRITICAL, ANUBIS_DENY, KNOWN_BOT
  • Labels négatifs (légitime) : NORMAL, LEGITIMATE_BROWSER
  • Activation requiert ≥ XGB_MIN_LABELS (100) labels
  • Réentraînement tous les XGB_RETRAIN_INTERVAL_HOURS (168h = 7 jours)
  • Filtrage Cleanlab : avant l'entraînement, un XGBoost rapide (80 arbres, 3-fold CV) produit des pred_probs qui alimentent cleanlab.filter.find_label_issues(). Les exemples identifiés comme bruités sont exclus du jeu d'entraînement. En cas d'échec, les labels bruts sont conservés.

Détection multifactorielle des navigateurs

Module browser.py — classifie chaque session sur 5 axes pondérés :

Axe Clé Poids Composantes
1 — JA4 connu axis_ja4_known 0.25 Famille navigateur identifiée dans dict_browser_ja4 → 1.0, sinon 0.0
2 — Structure JA4 axis_ja4_struct 0.15 TLS 1.3 (×0.35), h2/h3 (×0.25), nb ciphers 1025 (×0.20), nb extensions 1025 (×0.20)
3 — HTTP moderne axis_http_modern 0.25 modern_browser_score ≥ 50 (×0.35), Accept-Language (×0.20), Sec-Fetch < 0.3 (×0.25), generic_accept < 0.3 (×0.10), pas de ua_ch_mismatch (×0.10)
4 — Comportement navigation axis_nav_behavior 0.15 has_cookie (×0.25), has_referer (×0.25), asset_ratio > 0.15 (×0.25), direct_access < 0.5 (×0.25)
5 — Cohérence TLS/TCP axis_tls_coherence 0.20 Pas d'alpn_mismatch (×0.25), window_scale OK (×0.20), tls12 < 0.1 (×0.20), pas d'http10 (×0.15), ALPN présent (×0.20)

Seuil : browser_confidence ≥ 0.55 + famille identifiée → LEGITIMATE_BROWSER

Propagation par cohorte : si ≥ 70 % des sessions partageant un JA4 sont classées navigateur, les sessions NORMAL/LOW restantes avec le même JA4 sont aussi classées LEGITIMATE_BROWSER.

Inférence de famille : pour les JA4 inconnus, correspondance structurelle avec les profils _BROWSER_JA4_PROFILES (Chromium, Firefox, Safari, Tor_Browser) — requiert browser_confidence ≥ 0.45.


Scoring et normalisation

Normalisation des scores (normalize_scores)

Les scores bruts (négatifs = anomalous) sont mappés vers [0, 1] avec 1 = le plus anomalous :

result[mask] = clip(scores / (s_min + 1e-9), 0, 1)

Les scores ≥ 0 sont mis à 0.

Seuil adaptatif (compute_adaptive_threshold)

threshold = min(percentile(scores_négatifs, ANOMALY_PERCENTILE), ANOMALY_THRESHOLD)

Avec ANOMALY_PERCENTILE=5 et ANOMALY_THRESHOLD=-0.05.

Pénalité de récurrence

Appliquée au raw_anomaly_score :

raw_anomaly_score = log1p(recurrence_count) × RECURRENCE_WEIGHT

Niveaux de menace

Plage de score brut Niveau Interprétation
< 0.30 CRITICAL Comportement extrêmement anomalous
< 0.15 HIGH Signal d'anomalie fort
< 0.05 MEDIUM Anomalie modérée
< 0 LOW Légèrement inhabituel
≥ 0 NORMAL Trafic normal

Clustering HDBSCAN des campagnes

Lorsque ENABLE_CLUSTERING=true, les anomalies sont regroupées en campagnes par HDBSCAN :

hdbscan.HDBSCAN(
    min_cluster_size=CLUSTERING_MIN_SAMPLES,      # défaut : 3
    min_samples=max(2, CLUSTERING_MIN_SAMPLES  1),  # défaut : 2
    cluster_selection_method='eom',
)

Espace de clustering : si un NFEnsemble est disponible, le clustering s'effectue dans l'espace latent moyen du Deep Ensemble. Sinon, StandardScaler est appliqué sur les features brutes.

Fallback si hdbscan n'est pas disponible : DBSCAN(eps=0.5, min_samples=CLUSTERING_MIN_SAMPLES).

Chaque anomalie reçoit un campaign_id (1 = pas de cluster).


Liste des features

Features communes — modèle Applicatif (73 features)

Comportement HTTP

Feature Description
hits Nombre de requêtes dans la fenêtre
hit_velocity Requêtes par seconde
fuzzing_index Score de diversité chemins/paramètres
post_ratio Fraction de requêtes POST
port_exhaustion_ratio Fraction de ports source distincts / total
orphan_ratio Requêtes sans corrélation TLS
head_ratio Fraction de requêtes HEAD
http10_ratio Fraction de requêtes HTTP/1.0
generic_accept_ratio Fraction d'en-têtes Accept courts
sec_fetch_absence_rate Fraction sans Sec-Fetch-Site
missing_accept_enc_ratio Fraction sans Accept-Encoding
http_scheme_ratio Fraction utilisant HTTP (pas HTTPS)

Gestion de connexion

Feature Description
max_keepalives Max de requêtes sur une seule connexion Keep-Alive
tcp_shared_count Connexions TCP partagées entre sessions
multiplexing_efficiency Efficacité du multiplexage HTTP/2

Empreinte navigateur

Feature Description
header_count Nombre d'en-têtes HTTP envoyés
has_accept_language Présence de l'en-tête Accept-Language
has_cookie Présence de l'en-tête Cookie
has_referer Présence de l'en-tête Referer
modern_browser_score Score composite de conformité navigateur (0100)
ua_ch_mismatch Incohérence User-Agent vs Client Hints
ip_id_zero_ratio Paquets IP avec ID=0 (pile minimaliste/headless)
header_order_shared_count IPs partageant le même ordre d'en-têtes
header_order_confidence Entropie normalisée de l'ordre des en-têtes
distinct_header_orders Ordres d'en-têtes distincts par IP
is_fake_navigation Sec-Fetch-Mode=navigate avec destination non-document

Comportement de navigation

Feature Description
request_size_variance Variance de la taille des requêtes
mss_mobile_mismatch Incohérence TCP MSS vs profil mobile
asset_ratio Fraction de requêtes de ressources statiques
direct_access_ratio Accès directs (sans referer)
is_ua_rotating Rotation de User-Agent détectée
distinct_ja4_count Empreintes JA4 distinctes par IP
anomalous_payload_ratio Fraction de charges utiles anomalous

Concentration et rareté

Feature Description
src_port_density Entropie des ports source
ja4_asn_concentration Concentration JA4 au sein de l'ASN
ja4_country_concentration Concentration JA4 par pays
is_rare_ja4 Empreinte JA4 rare (< 100 hits totaux)

Temporel et diversité

Feature Description
temporal_entropy Entropie de la distribution temporelle
path_diversity_ratio Diversité des chemins URL
url_depth_variance Variance de la profondeur des URL

Anubis

Feature Description
anubis_is_flagged Signal de suspicion Anubis (bot détecté, action ni ALLOW/DENY/vide)

Navigateur multifactoriel — 6 axes

Feature Description
is_known_browser JA4 correspond à un navigateur connu
browser_consistency_score Score composite de cohérence navigateur
browser_confidence Confiance globale de l'identification navigateur (seuil 0.55)
axis_ja4_known Axe 1 : JA4 reconnu dans dict_browser_ja4
axis_ja4_struct Axe 2 : structure JA4 cohérente (chiffrement, extensions TLS)
axis_http_modern Axe 3 : comportement HTTP moderne (Sec-Fetch-*, Accept-Language)
axis_nav_behavior Axe 4 : comportement de navigation (asset_ratio, referer, cascade)
axis_tls_coherence Axe 5 : cohérence TLS/TCP (ALPN, SNI, MSS mobile)
axis_h2_coherence Axe 6 : cohérence HTTP/2 (h2_fingerprint ↔ famille navigateur déclarée)

Thèse §5 — Features avancées

Feature Description
seq_emb_0..seq_emb_31 Embeddings séquentiels via Transformer (§5.2, remplace path_transition_entropy + cadence_cv)
burst_ratio Fraction de requêtes en rafale (§5.3)
pause_ratio Fraction de pauses longues (§5.3)
lag1_autocorrelation Autocorrélation lag-1 des inter-arrivées (§5.3)
benford_deviation Déviation par rapport à la loi de Benford
root_to_first_asset_delay Délai HTML → premier asset (§5.4, détection headless)
asset_load_stddev Écart-type des intervalles de chargement d'assets (§5.4)
host_diversity Diversité des hôtes ciblés (§5.8)
host_sweep_speed Vitesse de balayage des hôtes (§5.8)
host_coverage_uniformity Uniformité de couverture des hôtes (§5.8)
cross_domain_path_similarity Similarité Jaccard inter-hôte des chemins — scanner latéral (§5.8)

Features HTTP/2

Feature Description
h2_settings_known Fingerprint HTTP/2 reconnu dans dict_browser_h2 (0/1)
h2_pseudo_order_match Ordre des pseudo-headers cohérent avec le navigateur déclaré (0/1)
h2_ja4_coherence Cohérence HTTP/2 ↔ JA4 (même famille) (0/1)
h2_settings_rare Fingerprint HTTP/2 avec <100 occurrences globales (0/1)

Score de cohérence cross-layer

Feature Description
fingerprint_coherence_score Score composite (0.01.0) combinant : JA4↔UA, H2↔JA4, TCP↔UA, langue↔ASN, Sec-CH-UA↔UA

Features TCP fenêtre

Feature Description
true_window_size Taille réelle de la fenêtre TCP
window_mss_ratio Ratio fenêtre TCP / MSS

Features de détection avancées

Feature Description
has_xff Présence de l'en-tête X-Forwarded-For
unusual_content_type_ratio Fraction de Content-Type inhabituels
non_standard_port_ratio Fraction de ports non standards
login_post_concentration Concentration de POST sur les pages de login
sec_ch_mobile_mismatch Incohérence Sec-CH-UA-Mobile

Features supplémentaires — modèle Complet (+12 features TCP/TLS)

Feature Description
tcp_jitter_variance Variance du jitter TCP inter-paquets
alpn_http_mismatch Incohérence ALPN vs protocole HTTP réel
is_alpn_missing ALPN absent dans le ClientHello
sni_host_mismatch Incohérence TLS SNI vs en-tête Host HTTP
ja3_diversity_ratio Ratio de diversité JA3 par IP
syn_timing_cv Coefficient de variation du timing SYN
tls12_ratio Fraction de connexions TLS 1.2
ip_df_variance Variance du flag Don't-Fragment IP
avg_ttl TTL IP moyen (empreinte OS)
ttl_std Écart-type du TTL
no_window_scale_ratio Fraction sans TCP Window Scale
ja4_drift_ratio Dérive JA4 intra-session (§5.5)

Pipeline de détection

1. Requête view_ai_features_1h → DataFrame
2. Enrichissement optionnel view_thesis_features_1h (features thèse §5)
3. Prétraitement : preprocess_df() (nettoyage, browser axes 6, imputation)
4. Chargement du feedback SOC → reclassification
5. Chargement de la carte de récurrence (view_ip_recurrence)
6. Séparation par correlated = 1 / correlated = 0
7. Pour chaque modèle (Complet, Applicatif) :
   a. Validation des features (exclure constantes/manquantes)
   b. Séparation des bots connus → journalisation KNOWN_BOT
   c. Filtrage de la baseline humaine (asn_label = 'human', fingerprint_coherence_score ≥ seuil)
   d. Chargement ou entraînement EIF + NFEnsemble
   e. Scoring du trafic inconnu (EIF + NFEnsemble, incertitude inter-modèles)
   f. Chargement ou entraînement XGBoost (si labels disponibles)
   g. MetaLearner : pondération apprise (logistique) sur historique SOC, sinon fallback poids fixes
   h. Combinaison des scores via MetaLearner ou formule fixe
   i. Normalisation [0, 1]
   j. Seuil adaptatif (percentile_5 des scores négatifs, minimum -0.05)
   k. Pénalité de récurrence
   l. ExIFFI (importance par profondeur d'isolation EIF) + erreur NF par feature
   m. SHAP top-5 TreeExplainer
   n. HDBSCAN clustering → campaign_id
   o. Détection de dérive (ADWIN + KS test + KL divergence)
   p. Alerte drift adversarial (dérive simultanée multiple features → direction commune, ou incertitude NF > NF_UNCERTAINTY_THRESHOLD)
8. Analyse de flotte (fleet.py) : graphe bipartite JA4×ASN → communautés GraphSAGE → fleet_score
9. Scoring dynamique H2 (browser_matcher_dynamic.py) : profils auto-appris vs sessions entrantes
10. Mode multi-fenêtre (si activé) : idem sur view_ai_features_24h
11. Insertion → ml_all_scores (toutes les sessions scorées)
12. Déduplication intra-cycle (garder raw_anomaly_score le plus bas par IP)
13. Déduplication inter-cycle (TTL, skip si détecté récemment sauf aggravation ≥ 0.05)
14. Insertion → ml_detected_anomalies (anomalies filtrées)
15. Insertion → fleet_detections (flottes détectées avec fleet_score)
16. Enregistrement → ml_performance_metrics (métriques de cycle + alertes)

Détection de dérive (ADWIN + KS + KL divergence)

Par feature, trois mécanismes comparent la distribution courante avec la distribution d'entraînement :

ADWIN (Adaptive Windowing) (River) :

  • Détection en ligne par fenêtre adaptative — chaque feature est surveillée par un détecteur ADWIN indépendant
  • Propriété drift_detected (bool) — vrai quand un changement de distribution est détecté dans la fenêtre
  • Pas de seuil manuel — ADWIN ajuste automatiquement la taille de fenêtre
  • Remplace la dérive KS+KL pour les features continues en temps réel

Test KS (Kolmogorov-Smirnov) :

  • Distribution reconstruite par interpolation à partir d'un digest quantile 9 points (p5, p10, p25, p50, p75, p90, p95)
  • Feature driftée si p_value < 0.05

Divergence KL (Kullback-Leibler) :

  • Histogramme discrétisé (20 bins) de la distribution courante vs baseline
  • Feature driftée si KL > seuil (0.5 par défaut)
  • Détection de drift adversarial : si ≥30% des features dérivent simultanément dans la même direction → alerte ADVERSARIAL_DRIFT. Également déclenché quand nf_uncertainty > NF_UNCERTAINTY_THRESHOLD → alerte ADVERSARIAL_DRIFT_NF

Règle de décision : une feature est en drift si ADWIN détecte un changement, ou si KS ou KL dépasse son seuil.

  • Dérive globale = fraction de features driftées
  • Si drift > DRIFT_THRESHOLD (0.30) → réentraînement automatique

Fallback (sans scipy) : méthode Z-score — feature driftée si |moyenne_courante moyenne_entraînement| / std_entraînement > 2.0.


MetaLearner

Remplace la pondération linéaire fixe (1-XGB_W)×((1-AE_W)×eif + AE_W×ae) + XGB_W×xgb par un MLP appris (scoring.MetaLearner) :

P(bot) = MLP(w1×eif + w2×ae + w3×xgb + w4×volume + w5×correlated + bias)
  • Architecture : MLP (Multi-Layer Perceptron) avec couches cachées — remplace la régression logistique
  • Entraînement : sur l'historique ml_all_scores JOIN soc_feedback (labels SOC + KNOWN_BOT + ANUBIS_DENY + LEGITIMATE_BROWSER)
  • Seuil : activé seulement si ≥1000 labels disponibles — sinon fallback aux poids fixes
  • Transparence : poids appris journalisés dans ml_performance_metrics pour audit SOC
  • Validation : comparaison MetaLearner vs poids fixes sur split temporel (données postérieures à l'entraînement)

ExIFFI et explicabilité augmentée

En complément de SHAP, le module expose deux méthodes d'importance de features spécifiques aux modèles utilisés :

ExIFFI (compute_exiffi_importance) :

  • Importance calculée par la profondeur moyenne d'isolation par feature dans l'EIF
  • Une feature avec profondeur d'isolation faible contribue fortement à l'anomalie
  • Corrèle avec SHAP mais capte des aspects complémentaires de la structure EIF

Erreur NF par feature (compute_ae_feature_errors) :

  • Score de reconstruction NF feature par feature : err_i = (x_i - x̂_i)² dans l'espace latent
  • Pour chaque anomalie, les features avec la plus grande erreur de reconstruction sont identifiées
  • Expose quelles dimensions le Normalizing Flow ne parvient pas à reconstruire

Les deux méthodes sont disponibles dans le champ shap_features des résultats, en complément des valeurs SHAP TreeExplainer.


Détection de flottes (fleet.py)

Détecte les botnets coordonnés utilisant des JA4 et ASN rotatifs via analyse de graphe bipartite :

  1. Construction du graphe : nœuds JA4 ASN, arêtes IP observées dans le cycle
  2. Embedding : PyTorch Geometric GraphSAGE pour apprendre des embeddings de nœuds
  3. Communautés : clustering dans l'espace d'embedding GraphSAGE
  4. Score de flotte : fleet_score = taille_communauté × densité_arêtes / log(nb_ASN)
  5. Enrichissement : les IPs membres reçoivent un malus proportionnel au fleet_score

Résultats stockés dans fleet_detections (TTL 7 jours). Exposés dans le dashboard via la page /fleet.


Métriques de performance (metrics.py)

Enregistre par cycle dans ml_performance_metrics :

Métrique Description
anomaly_rate Taux d'anomalies détectées (cible : 0.5%10%)
known_bot_rate Fraction KNOWN_BOT dans le cycle
legit_browser_rate Fraction LEGITIMATE_BROWSER
drift_rate Fraction de features en dérive
corr_rate Taux de sessions corrélées (cible : ≥50%)
cycle_latency_s Durée totale d'inférence (cible : <300s)
alert_flags Alertes émises (CALIBRATION_HIGH/LOW, DRIFT, CORRELATION, LATENCY, ADVERSARIAL_DRIFT_NF)

Seuils d'alerte :

  • anomaly_rate > 10%CALIBRATION_HIGH
  • anomaly_rate < 0.5%CALIBRATION_LOW
  • drift_rate > 30%DRIFT_ALERT
  • corr_rate < 50%CORRELATION_ALERT
  • cycle_latency_s > 300LATENCY_ALERT

Enrichissement Anubis

La vue view_ai_features_1h enrichit chaque IP via les dictionnaires Anubis selon une cascade de priorité :

  1. IP/CIDR — correspondance exacte d'adresse ou de sous-réseau
  2. ASN — correspondance par numéro d'ASN

Tables de sortie

ml_detected_anomalies

Détections d'anomalies au-dessus du seuil de menace. Engine : ReplacingMergeTree(detected_at), ORDER BY (src_ip, model_name), TTL 7 jours.

Colonnes clés : detected_at, src_ip, ja4, host, bot_name, anomaly_score, raw_anomaly_score, threat_level, model_name, recurrence, campaign_id, reason, anubis_bot_name, anubis_bot_action, anubis_bot_category, plus toutes les features ML.

ml_all_scores

Toutes les classifications (sans filtre de seuil) pour l'observabilité. Engine : ReplacingMergeTree(detected_at), ORDER BY (window_start, src_ip, ja4, host, model_name), TTL 7 jours.


Format du journal de décisions

Le fichier decisions.jsonl contient des entrées JSONL structurées :

{"event": "CYCLE_START", "cycle_id": "20260309T143000", "total": 5000, "human": 1500, "known_bot": 200, "correlated": 3000}
{"event": "ANOMALY", "src_ip": "203.0.113.42", "score": -0.25, "threat_level": "HIGH", "reason": "hit_velocity=45.2, fuzzing_index=0.8, ...", "campaign_id": 3}
{"event": "KNOWN_BOT", "src_ip": "198.51.100.10", "bot_name": "AhrefsBot"}
{"event": "CYCLE_END", "cycle_id": "20260309T143000", "anomalies": 15, "known_bots": 200, "duration_sec": 12.5}

Rotation des logs : 50 Mo max × LOG_BACKUP_COUNT sauvegardes (défaut : 7).


Point de santé

  • URL : GET http://localhost:8080/
  • Réponses : 200 OK (corps OK) ou 503 Service Unavailable (corps DEGRADED)
  • Exécuté dans un thread daemon au démarrage
  • État mis à False après MAX_CONSECUTIVE_FAILURES (3) échecs consécutifs ClickHouse, remis à True dès le premier succès

Persistance des modèles

Fichier Description
model_<name>_<version>.joblib EIF sérialisé (joblib)
model_<name>_<version>.meta.json Métadonnées (features, seuils, statistiques d'entraînement, digest quantile)
model_<name>.current Pointeur vers la version active
training_history.jsonl Historique d'entraînement

Rotation automatique : seules les MODEL_HISTORY_COUNT dernières versions (défaut : 10) sont conservées.


Déploiement Docker

# Construction de l'image
make build-bot-detector

# Exécution avec docker-compose
cd services/bot-detector
docker-compose up -d

# Tests
make test-bot-detector

Volumes

Chemin hôte Chemin conteneur Description
./bot_detector_logs /var/log/bot_detector Journaux de décisions (JSONL)
./bot_detector_models /var/lib/bot_detector Modèles ML persistés
./reputation/data/user_files/bot_ip.csv /data/bot_ip.csv (ro) Liste d'IPs de bots connus
./reputation/data/user_files/bot_ja4.csv /data/bot_ja4.csv (ro) Liste de JA4 de bots connus
./reputation/data/user_files/asn_reputation.csv /data/asn_reputation.csv (ro) Labels de réputation ASN