Implement offline profile building (profile_builder.py) and real-time dynamic scoring (browser_matcher_dynamic.py) using HDBSCAN-based browser fingerprint clustering. Add ClickHouse materialized view (13_h2_profiling.sql) for h2_profile_stats aggregation. Update thesis and project documentation to cover the new dynamic profiling architecture. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
649 lines
26 KiB
Markdown
649 lines
26 KiB
Markdown
# Bot Detector IA — Axes d'amélioration
|
||
|
||
> Suivi d'implémentation — mis à jour le 2026-04-13 | Architecture modulaire (16 modules)
|
||
|
||
---
|
||
|
||
## Résumé des axes proposés — État d'implémentation
|
||
|
||
| # | Axe | Statut | Implémentation |
|
||
|---|-----|--------|----------------|
|
||
| A1 | [Détection de dérive conceptuelle](#a1--détection-de-dérive-conceptuelle) | ✅ IMPLÉMENTÉ | `scoring.py` : `_compute_drift_score()` + fallback z-score |
|
||
| A2 | [Seuil adaptatif par percentile](#a2--seuil-adaptatif-par-percentile) | ✅ IMPLÉMENTÉ | `scoring.py` : `compute_adaptive_threshold()` |
|
||
| A3 | [Analyse multi-fenêtres temporelles](#a3--analyse-multi-fenêtres-temporelles) | ✅ IMPLÉMENTÉ | `cycle.py` : `ENABLE_MULTIWINDOW` + `Complet_24h`/`Applicatif_24h` |
|
||
| A4 | [Explainabilité par SHAP](#a4--explainabilité-par-shap) | ✅ IMPLÉMENTÉ | `scoring.py` : `_compute_shap_top_features()`, top-5 |
|
||
| A5 | [Déduplication avec TTL inter-cycles](#a5--déduplication-avec-ttl-inter-cycles) | ✅ IMPLÉMENTÉ | `cycle.py` : `_filter_recent_detections()`, `DEDUP_TTL_MIN=60` |
|
||
| A6 | [Pondération par récurrence dans le score](#a6--pondération-par-récurrence-dans-le-score) | ✅ IMPLÉMENTÉ | `pipeline.py` : `raw -= log1p(count) * RECURRENCE_WEIGHT` |
|
||
| A7 | [Validation de complétude des features](#a7--validation-de-complétude-des-features) | ✅ IMPLÉMENTÉ | `scoring.py` : `validate_features()`, `MIN_VALID_FEATURE_RATIO=0.50` |
|
||
| A8 | [Clustering comportemental des anomalies](#a8--clustering-comportemental-des-anomalies) | ✅ IMPLÉMENTÉ | `scoring.py` : HDBSCAN + escalade campagne dans `pipeline.py` |
|
||
| A9 | [Métriques Prometheus / health check enrichi](#a9--métriques-prometheus--health-check-enrichi) | ❌ À FAIRE | Health check binaire uniquement (`infra.py` : 200/503) |
|
||
| A10 | [Normalisation des scores entre modèles](#a10--normalisation-des-scores-entre-modèles) | ✅ IMPLÉMENTÉ | `scoring.py` : `normalize_scores()` min-max [0, 1] |
|
||
|
||
---
|
||
|
||
## A1 — Détection de dérive conceptuelle
|
||
|
||
### ✅ IMPLÉMENTÉ
|
||
|
||
**Module** : `scoring.py` — fonctions `_compute_drift_score()` et `_compute_drift_score_zscore()`
|
||
|
||
**Différences avec la proposition initiale** :
|
||
|
||
| Proposition | Implémentation |
|
||
|-------------|----------------|
|
||
| KS-test (scipy `ks_2samp`) | Méthode principale : comparaison par quantiles interpolés (KS-like sans scipy). Fallback : z-score `\|μ_current - μ_trained\| / σ_trained > 2.0` |
|
||
| Seuil par `p_value < 0.05` | Même seuil p < 0.05 pour la méthode quantile ; z > 2.0 pour le fallback |
|
||
| `DRIFT_THRESHOLD` (30%) | ✅ Identique : `DRIFT_THRESHOLD = 0.30` (fraction de features en dérive) |
|
||
| Sauvegarde dans `.meta.json` | ✅ `baseline_stats` avec `{mean, std, p25, p75}` par feature |
|
||
| Événement `DRIFT_DETECTED` | ✅ Journalisé avec la liste des features déroutantes |
|
||
|
||
**Appelé depuis** : `models.py` → `load_or_train_model()`, avant la décision de chargement vs retrain.
|
||
|
||
### Problème
|
||
|
||
L'Isolation Forest est entraîné sur la baseline humaine courante. Si le profil du trafic légitime évolue graduellement (nouveau navigateur populaire, changement de comportement utilisateur, migration réseau), le modèle vieilli peut :
|
||
- Générer des **faux positifs** sur du trafic humain nouvellement apparu
|
||
- Rater des **faux négatifs** si les bots imitent les anciens patterns
|
||
|
||
### Références
|
||
- Gama et al. (2014) — *A Survey on Concept Drift Adaptation*
|
||
- Rabanser et al. (2019) — *Failing Loudly: An Empirical Study of Methods for Detecting Dataset Shift*
|
||
|
||
---
|
||
|
||
## A2 — Seuil adaptatif par percentile
|
||
|
||
### ✅ IMPLÉMENTÉ
|
||
|
||
**Module** : `scoring.py` — fonction `compute_adaptive_threshold()`
|
||
|
||
**Implémentation conforme à la proposition** :
|
||
|
||
```python
|
||
neg_scores = scores[scores < 0]
|
||
adaptive = np.percentile(neg_scores, ANOMALY_PERCENTILE) # défaut : 5
|
||
effective_threshold = min(adaptive, ANOMALY_THRESHOLD) # garde-fou : -0.05
|
||
```
|
||
|
||
| Proposition | Implémentation |
|
||
|-------------|----------------|
|
||
| `ANOMALY_PERCENTILE` (0–20, défaut 5) | ✅ `config.py` : `ANOMALY_PERCENTILE = 5` |
|
||
| Seuil statique en garde-fou | ✅ `ANOMALY_THRESHOLD = -0.05` |
|
||
| Log du seuil effectif dans `ANOMALY` | ✅ Journalisé dans les événements |
|
||
|
||
**Appelé depuis** : `pipeline.py` ligne 144, sur les scores bruts EIF uniquement.
|
||
|
||
---
|
||
|
||
## A3 — Analyse multi-fenêtres temporelles
|
||
|
||
### ✅ IMPLÉMENTÉ
|
||
|
||
**Module** : `cycle.py` — dans `fetch_and_analyze()`, lignes 257–275
|
||
|
||
**Implémentation** :
|
||
|
||
| Proposition | Implémentation |
|
||
|-------------|----------------|
|
||
| Vue 24h dans ClickHouse | ✅ `MULTIWINDOW_VIEW = 'view_ai_features_24h'` (configurable) |
|
||
| Deux modèles supplémentaires | ✅ `Complet_24h` et `Applicatif_24h` |
|
||
| Combinaison AND logique | Implémenté en **OR logique** : une IP est flaggée si anomalie dans ≥ 1 fenêtre |
|
||
| Score le plus bas conservé | ✅ En cas de doublon, le score le plus bas (le plus anormal) est conservé |
|
||
| Activation par feature flag | ✅ `ENABLE_MULTIWINDOW = false` par défaut |
|
||
|
||
**Note** : La proposition originale suggérait un AND logique (anomalie dans les deux fenêtres). L'implémentation utilise un OR pour maximiser la couverture, avec déduplication par score minimal.
|
||
|
||
---
|
||
|
||
## A4 — Explainabilité par SHAP
|
||
|
||
### ✅ IMPLÉMENTÉ
|
||
|
||
**Module** : `scoring.py` — fonction `_compute_shap_top_features()`
|
||
|
||
| Proposition | Implémentation |
|
||
|-------------|----------------|
|
||
| `TreeExplainer` (sklearn IF) | ✅ Pour sklearn : `shap.TreeExplainer` |
|
||
| Top-5 features les plus contributives | ✅ Top-5 features avec valeurs SHAP |
|
||
| `ENABLE_SHAP=true/false` | ✅ `config.py` : `ENABLE_SHAP = true` (ET `SHAP_AVAILABLE`) |
|
||
| SHAP uniquement sur les anomalies | ✅ Calculé uniquement pour les IPs flaggées |
|
||
| Enrichissement du champ `reason` | ✅ Via `_build_reason()` — SHAP intégré dans la raison textuelle |
|
||
|
||
**Ajout par rapport à la proposition** : Pour isotree (Extended IF), SHAP utilise
|
||
`PermutationExplainer` au lieu de `TreeExplainer` (isotree n'est pas nativement
|
||
supporté par TreeSHAP).
|
||
|
||
**Appelé depuis** : `pipeline.py` lignes 298–303, après l'extraction des anomalies.
|
||
|
||
### Références
|
||
- Lundberg & Lee (2017) — *A Unified Approach to Interpreting Model Predictions*
|
||
|
||
---
|
||
|
||
## A5 — Déduplication avec TTL inter-cycles
|
||
|
||
### ✅ IMPLÉMENTÉ
|
||
|
||
**Module** : `cycle.py` — fonction `_filter_recent_detections()`
|
||
|
||
| Proposition | Implémentation |
|
||
|-------------|----------------|
|
||
| `DEDUP_TTL_MIN` (défaut 60 min) | ✅ `config.py` : `DEDUP_TTL_MIN = 60` |
|
||
| Requête ClickHouse pour les IPs récentes | ✅ Interroge `ml_detected_anomalies` dans les dernières `DEDUP_TTL_MIN` minutes |
|
||
| Variante : réinsertion si dégradation ≥ 0.05 | ✅ Réinsertion uniquement si le score s'est dégradé de ≥ 0.05 points |
|
||
| `DEDUP_TTL_MIN=0` pour désactiver | ✅ Conforme |
|
||
| Déduplication intra-cycle (`drop_duplicates`) | ✅ `cycle.py` ligne 302 : `drop_duplicates(subset=['src_ip'], keep='first')` |
|
||
|
||
---
|
||
|
||
## A6 — Pondération par récurrence dans le score
|
||
|
||
### ✅ IMPLÉMENTÉ
|
||
|
||
**Module** : `pipeline.py` — lignes 148–151
|
||
|
||
| Proposition | Implémentation |
|
||
|-------------|----------------|
|
||
| `RECURRENCE_WEIGHT = 0.005` | ✅ `config.py` : `RECURRENCE_WEIGHT = 0.005` |
|
||
| `adjusted = score - log1p(recurrence) * weight` | ✅ `raw_anomaly_score -= log1p(recurrence_count) * RECURRENCE_WEIGHT` |
|
||
| Récurrence chargée depuis ClickHouse | ✅ `view_ip_recurrence` dans `cycle.py` ligne 215 |
|
||
| Stockage séparé raw_score / adjusted_score | Le score ajusté remplace `raw_anomaly_score` (pas de stockage séparé) |
|
||
|
||
---
|
||
|
||
## A7 — Validation de complétude des features
|
||
|
||
### ✅ IMPLÉMENTÉ
|
||
|
||
**Module** : `scoring.py` — fonction `validate_features()`
|
||
|
||
| Proposition | Implémentation |
|
||
|-------------|----------------|
|
||
| Détection des features absentes | ✅ Features manquantes du DataFrame |
|
||
| Détection des features constantes (std=0) | ✅ Features à std=0 exclues |
|
||
| `MIN_VALID_FEATURE_RATIO` (défaut 0.8) | Implémenté à **0.50** (seuil moins strict) |
|
||
| Événement `FEATURE_WARNING` | ✅ Journalisé dans le JSONL |
|
||
| `SKIPPED_INVALID_FEATURES` si ratio insuffisant | ✅ Retourne `None` → cycle ignoré |
|
||
|
||
**Ajouts par rapport à la proposition** :
|
||
- Détection des features **entièrement à zéro** (pipeline non alimenté) en plus des features constantes.
|
||
- Exclusions structurelles par modèle (`STRUCTURAL_EXCLUDED_FEATURES` dans `config.py`) :
|
||
le modèle Applicatif exclut automatiquement 15 features TCP/TLS non disponibles sans corrélation.
|
||
|
||
---
|
||
|
||
## A8 — Clustering comportemental des anomalies
|
||
|
||
### ✅ IMPLÉMENTÉ
|
||
|
||
**Module** : `scoring.py` — fonction `_cluster_anomalies()` + `pipeline.py` pour l'escalade
|
||
|
||
| Proposition | Implémentation |
|
||
|-------------|----------------|
|
||
| DBSCAN (eps=0.5, min_samples=3) | Utilisé en **fallback**. Algorithme principal : **HDBSCAN** |
|
||
| `campaign_id` dans les événements | ✅ Journalisé dans les événements `ANOMALY` et inséré dans `ml_detected_anomalies` |
|
||
| `eps` et `min_samples` configurables | ✅ `CLUSTERING_MIN_SAMPLES = 3`, `ENABLE_CLUSTERING = true` |
|
||
| Activation conditionnelle | ✅ `ENABLE_CLUSTERING` (feature flag) |
|
||
|
||
**Améliorations par rapport à la proposition** :
|
||
|
||
1. **HDBSCAN au lieu de DBSCAN** : Utilise `HDBSCAN(min_cluster_size=3, min_samples=2,
|
||
cluster_selection_method='eom')` via import optionnel `hdbscan`. Fallback DBSCAN
|
||
si `hdbscan` non installé.
|
||
|
||
2. **Clustering dans l'espace latent** : Si un modèle Autoencoder est disponible,
|
||
le clustering opère dans l'espace latent (dimension 16) plutôt que sur les
|
||
features brutes — meilleure séparation des clusters.
|
||
|
||
3. **Escalade de campagne** (`pipeline.py` lignes 311–324) : Les IPs d'un cluster
|
||
≥ 5 membres voient leur threat level escaladé de `HIGH` → `CRITICAL`.
|
||
|
||
### Références
|
||
- Ester et al. (1996) — *A Density-Based Algorithm for Discovering Clusters in Large Spatial Databases*
|
||
- Campello et al. (2013) — *Density-Based Clustering Based on Hierarchical Density Estimates* (HDBSCAN)
|
||
|
||
---
|
||
|
||
## A9 — Métriques Prometheus / health check enrichi
|
||
|
||
### ❌ À FAIRE
|
||
|
||
**État actuel** : Le health check est un serveur HTTP binaire dans `infra.py`
|
||
(`200 OK` / `503 DEGRADED`). Aucun endpoint `/metrics` n'est exposé.
|
||
|
||
**Ce qui manque** :
|
||
- Endpoint `/metrics` au format Prometheus text
|
||
- Métriques : durée du cycle, nombre d'anomalies par modèle, âge du modèle,
|
||
taille de la baseline, score de dérive
|
||
- Intégration Grafana/Alertmanager
|
||
|
||
**Prérequis** : Ajouter `prometheus_client` aux dépendances ou implémenter le
|
||
format texte manuellement sur le `HTTPServer` existant.
|
||
|
||
**Note** : La priorité est faible car les métriques clés sont déjà disponibles via
|
||
les événements `CYCLE_END` dans le JSONL et les tables ClickHouse (`ml_all_scores`).
|
||
|
||
### Proposition originale
|
||
|
||
Exposer un endpoint `/metrics` sur le même port que le health check :
|
||
|
||
```
|
||
botdetector_cycle_duration_seconds 12.4
|
||
botdetector_anomalies_total{model="Complet"} 3
|
||
botdetector_model_age_hours{model="Applicatif"} 0.91
|
||
botdetector_human_baseline_size{model="Applicatif"} 1725
|
||
```
|
||
|
||
---
|
||
|
||
## A10 — Normalisation des scores entre modèles
|
||
|
||
### ✅ IMPLÉMENTÉ
|
||
|
||
**Module** : `scoring.py` — fonction `normalize_scores()`
|
||
|
||
| Proposition | Implémentation |
|
||
|-------------|----------------|
|
||
| Min-max sur les scores < 0 | ✅ Normalisation min-max des scores négatifs |
|
||
| Intervalle [-1, 0] | Intervalle **[0, 1]** où 1 = le plus anormal |
|
||
| Threat levels sur score normalisé | Les threat levels sont toujours calculés sur le score **brut** IF (`score_to_threat_level()` dans `infra.py`) |
|
||
| Déduplication sur score normalisé | La déduplication utilise le score brut ajusté (avec pénalité de récurrence) |
|
||
|
||
**Note** : Le choix de conserver les threat levels sur le score brut (plutôt que
|
||
normalisé) assure la stabilité des seuils de classification. Le score normalisé
|
||
est utilisé pour le score final combiné (EIF+AE+XGB) et l'insertion dans
|
||
`ml_all_scores`.
|
||
|
||
---
|
||
|
||
## Notes d'implémentation générales
|
||
|
||
- **Compatibilité** : toute amélioration doit rester rétrocompatible avec le schéma `ml_detected_anomalies` existant (ajout de colonnes optionnelles uniquement)
|
||
- **Architecture modulaire** : le code est réparti en 16 modules (voir `DOCUMENTATION.md` §1.1), chaque amélioration touche un ou deux modules spécifiques
|
||
- **Tests** : 36 tests auto-contenus dans `tests/test_detector.py`, exécutables via `make test-bot-detector`
|
||
- **Feature flags** : les fonctionnalités sont activables via variables d'environnement (`ENABLE_SHAP`, `ENABLE_CLUSTERING`, `ENABLE_MULTIWINDOW`, `ENABLE_FEEDBACK`)
|
||
- **Imports optionnels** : `isotree`, `torch`, `xgboost`, `shap`, `hdbscan` sont tous optionnels avec fallbacks (`config.py`)
|
||
|
||
---
|
||
|
||
# Nouvelles dimensions de features — Propositions B
|
||
|
||
> Propositions de features supplémentaires pour l'ensemble ML, validées sur les données réelles de `ja4_processing`.
|
||
> Chaque proposition indique la force du signal observée en base, la source de données, la formule de calcul et les références scientifiques.
|
||
|
||
## Résumé des signaux — État d'implémentation
|
||
|
||
| # | Feature | Statut | Modèle | Implémentation |
|
||
|---|---------|--------|--------|----------------|
|
||
| B1 | `ja3_diversity_ratio` | ✅ IMPLÉMENTÉ | Complet | `FEATURES_COMPLET` dans `preprocessing.py` ligne 56 |
|
||
| B2 | `syn_timing_cv` | ✅ IMPLÉMENTÉ | Complet | `FEATURES_COMPLET` dans `preprocessing.py` ligne 56 |
|
||
| B3 | `tls12_ratio` | ✅ IMPLÉMENTÉ | Complet | `FEATURES_COMPLET` dans `preprocessing.py` ligne 56 |
|
||
| B4 | `head_ratio` | ✅ IMPLÉMENTÉ | Les deux | `FEATURES` dans `preprocessing.py` ligne 30 |
|
||
| B5 | `sec_fetch_absence_rate` | ✅ IMPLÉMENTÉ | Les deux | `FEATURES` dans `preprocessing.py` ligne 30 |
|
||
| B6 | `generic_accept_ratio` | ✅ IMPLÉMENTÉ | Les deux | `FEATURES` dans `preprocessing.py` ligne 30 |
|
||
| B7 | `http10_ratio` | ✅ IMPLÉMENTÉ | Les deux | `FEATURES` dans `preprocessing.py` ligne 30 |
|
||
| B8 | `ip_df_variance` | ✅ IMPLÉMENTÉ | Complet | `FEATURES_COMPLET` dans `preprocessing.py` ligne 56 |
|
||
| B9 | IP DF-bit consistency | 🔄 PARTIEL | Complet | Couvert par B8 (`ip_df_variance`), pas de feature séparée |
|
||
| B10 | JA4 concentration intra-ASN | ✅ IMPLÉMENTÉ (préexistant) | Les deux | `ja4_asn_concentration` dans `FEATURES` — existait avant les propositions B |
|
||
|
||
---
|
||
|
||
## B1 — JA3/JA4 Diversity Ratio (rotation de fingerprint TLS)
|
||
|
||
### ✅ IMPLÉMENTÉ
|
||
|
||
**Feature** : `ja3_diversity_ratio` — dans `FEATURES_COMPLET` (modèle Complet uniquement).
|
||
|
||
**Exclusion structurelle** : Exclue du modèle Applicatif via `STRUCTURAL_EXCLUDED_FEATURES`
|
||
dans `config.py`, car le JA3 n'est disponible que pour le trafic corrélé (L4/TLS).
|
||
|
||
**Détails d'implémentation** :
|
||
- La vue ClickHouse `view_ai_features_1h` calcule `uniqMerge(uniq_ja3) / greatest(uniq_ja4, 1)`
|
||
- Le MV `mv_agg_host_ip_ja4_1h` agrège `uniqState(ja3)` depuis `http_logs`
|
||
|
||
### Observation originale
|
||
|
||
```
|
||
185.177.72.60 → 1619 JA3 distincts / 2 JA4 → ratio 809.5
|
||
194.187.171.160 → 153 JA3 distincts / 2 JA4 → ratio 76.5
|
||
```
|
||
|
||
Le JA4 reste stable (il encode le type de client TLS + ALPN) mais le JA3 varie massivement. C'est la signature d'un **bot qui randomise les extensions TLS** pour contourner la détection par fingerprint.
|
||
|
||
### Références
|
||
|
||
- Siby et al. (2020) — *Encrypted DNS → Privacy? A Traffic Analysis Perspective*
|
||
- Anderson & McGrew (2016) — *Machine Learning for Encrypted Malware Traffic Classification*
|
||
- Husák et al. (2022) — *TLS fingerprinting for bot detection*
|
||
|
||
---
|
||
|
||
## B2 — SYN-to-ClientHello Timing Regularity
|
||
|
||
### ✅ IMPLÉMENTÉ
|
||
|
||
**Feature** : `syn_timing_cv` — dans `FEATURES_COMPLET` (modèle Complet uniquement).
|
||
|
||
**Implémentation** : Coefficient de variation (std/mean) du timing SYN→ClientHello,
|
||
calculé dans la vue ClickHouse : `sqrt(tcp_jitter_variance) / greatest(avg_syn_ms, 1)`.
|
||
|
||
### Observation originale
|
||
|
||
```
|
||
88.202.237.59 : 45 connexions, avg=22ms, std=0.00ms → timing robotique parfait
|
||
386/3222 IPs analysées (12%) ont une variance=0
|
||
```
|
||
|
||
### Références
|
||
|
||
- Zeber et al. (2020) — *The Measurement of Web Timing*
|
||
- Stevanovic & Pedersen (2015) — *Detecting Bots Using Multi-level Traffic Analysis*
|
||
|
||
---
|
||
|
||
## B3 — TLS 1.2 Exclusive Ratio
|
||
|
||
### ✅ IMPLÉMENTÉ
|
||
|
||
**Feature** : `tls12_ratio` — dans `FEATURES_COMPLET` (modèle Complet uniquement).
|
||
|
||
**Utilisation double** : En plus d'être une feature ML, `tls12_ratio` est utilisée
|
||
par l'**axe 5 (TLS/TCP Coherence)** de la détection navigateur dans `browser.py`
|
||
(condition `tls12_ratio < 0.1` pour un score positif).
|
||
|
||
### Observation originale
|
||
|
||
```
|
||
95.217.144.244 : 360/360 requêtes en TLS 1.2 (jamais TLS 1.3)
|
||
136 IPs utilisent exclusivement TLS 1.2 sur 3259 analysées (4.2%)
|
||
```
|
||
|
||
### Références
|
||
|
||
- Kotzias et al. (2018) — *Coming of Age: A Longitudinal Study of TLS Deployment*
|
||
- Cloudflare Radar 2024 — TLS 1.3 = 95%+ du trafic web mondial
|
||
|
||
---
|
||
|
||
## B4 — HEAD Method Ratio
|
||
|
||
### ✅ IMPLÉMENTÉ
|
||
|
||
**Feature** : `head_ratio` — dans `FEATURES` (les deux modèles).
|
||
|
||
**Commentaire dans le code** : `# B4-B7 : features L7 pures` (`preprocessing.py` ligne 29).
|
||
|
||
### Observation originale
|
||
|
||
```
|
||
34.140.199.84 : 11/12 requêtes HEAD (91.7%) → Google Cloud uptime checker
|
||
67/3335 IPs ont >50% de requêtes HEAD
|
||
```
|
||
|
||
### Références
|
||
|
||
- Barracuda Networks (2023) — *Bot Traffic Report*
|
||
- OWASP Automated Threat Handbook — OAT-011: Scraping, OAT-018: Credential Stuffing
|
||
|
||
---
|
||
|
||
## B5 — Sec-Fetch Absence Rate
|
||
|
||
### ✅ IMPLÉMENTÉ
|
||
|
||
**Feature** : `sec_fetch_absence_rate` — dans `FEATURES` (les deux modèles).
|
||
|
||
**Utilisation double** : Également utilisée par l'**axe 3 (HTTP Modern)** de la
|
||
détection navigateur dans `browser.py` (condition `sec_fetch_absence_rate < 0.3`
|
||
pour un score positif, poids 0.25).
|
||
|
||
### Observation originale
|
||
|
||
Les headers `Sec-Fetch-*` sont injectés automatiquement par les navigateurs modernes
|
||
(Chrome 76+, Firefox 90+). Leur absence signale un client non-navigateur.
|
||
|
||
### Références
|
||
|
||
- West & Loshbough (2019) — *Fetch Metadata Request Headers* (W3C Spec)
|
||
- Invernizzi et al. (2016) — *CLOAK of Visibility*
|
||
|
||
---
|
||
|
||
## B6 — Accept Header Entropy
|
||
|
||
### ✅ IMPLÉMENTÉ
|
||
|
||
**Feature** : `generic_accept_ratio` — dans `FEATURES` (les deux modèles).
|
||
|
||
**Note** : La proposition originale suggérait une entropie du header Accept.
|
||
L'implémentation utilise l'approche simplifiée (fraction de requêtes avec Accept
|
||
générique/vide), qui est plus robuste et moins coûteuse en calcul ClickHouse.
|
||
|
||
**Utilisation double** : Également utilisée par l'**axe 3 (HTTP Modern)** dans
|
||
`browser.py` (condition `generic_accept_ratio < 0.3`, poids 0.10).
|
||
|
||
### Références
|
||
|
||
- Nikiforakis et al. (2013) — *Cookieless Monster*
|
||
- Acar et al. (2014) — *The Web Never Forgets*
|
||
|
||
---
|
||
|
||
## B7 — HTTP/TLS Protocol Version Mismatch
|
||
|
||
### ✅ IMPLÉMENTÉ
|
||
|
||
**Feature** : `http10_ratio` — dans `FEATURES` (les deux modèles).
|
||
|
||
**Note** : La proposition originale contenait deux features (`http1_tls13_ratio`
|
||
et `http10_ratio`). Seule `http10_ratio` (fraction de requêtes HTTP/1.0) a été
|
||
implémentée — c'est le signal le plus fort car HTTP/1.0 est extrêmement rare dans
|
||
le trafic navigateur moderne.
|
||
|
||
**Utilisation double** : Également utilisée par l'**axe 5 (TLS/TCP Coherence)** dans
|
||
`browser.py` (condition `http10_ratio = 0`, poids 0.15).
|
||
|
||
---
|
||
|
||
## B8 — IP DF-Bit Consistency
|
||
|
||
### ✅ IMPLÉMENTÉ
|
||
|
||
**Feature** : `ip_df_variance` — dans `FEATURES_COMPLET` (modèle Complet uniquement).
|
||
|
||
**Implémentation** : `varPop(toFloat64(ip_meta_df))` dans la vue ClickHouse.
|
||
La variance du bit DF est combinée avec les features TTL (`avg_ttl`, `ttl_std`)
|
||
pour le TCP fingerprinting multi-dimensionnel.
|
||
|
||
---
|
||
|
||
## B9 — IP DF-Bit Consistency (proposition distincte)
|
||
|
||
### 🔄 PARTIEL
|
||
|
||
**État** : Couvert par B8. La proposition B9 était une variante de B8 avec une
|
||
analyse de cohérence intra-session supplémentaire. Cette variante n'a pas été
|
||
implémentée séparément — `ip_df_variance` couvre le cas d'usage principal.
|
||
|
||
---
|
||
|
||
## B10 — JA4 Concentration intra-ASN
|
||
|
||
### ✅ IMPLÉMENTÉ (préexistant)
|
||
|
||
**Feature** : `ja4_asn_concentration` — dans `FEATURES` (les deux modèles).
|
||
|
||
**Note** : Cette feature existait déjà avant les propositions B, dans la liste
|
||
initiale des features du modèle. Elle mesure la concentration d'un même fingerprint
|
||
JA4 au sein d'un ASN — un JA4 très concentré dans un seul ASN suggère un outil
|
||
déployé dans un datacenter spécifique.
|
||
|
||
---
|
||
|
||
## Récapitulatif des modifications ClickHouse réalisées
|
||
|
||
### Colonnes ajoutées dans `agg_host_ip_ja4_1h`
|
||
|
||
Les colonnes suivantes ont été ajoutées au MV et à la table d'agrégation pour
|
||
supporter les features B1–B8 :
|
||
|
||
```sql
|
||
-- B1 : JA3 diversity ratio
|
||
uniq_ja3 AggregateFunction(uniq, String)
|
||
-- B2 : SYN timing regularity
|
||
avg_syn_ms SimpleAggregateFunction(avg, Float64)
|
||
-- B3 : TLS 1.2 ratio
|
||
tls12_count SimpleAggregateFunction(sum, UInt64)
|
||
-- B4 : HEAD method ratio
|
||
count_head SimpleAggregateFunction(sum, UInt64)
|
||
-- B5 : Sec-Fetch absence
|
||
count_no_sec_fetch SimpleAggregateFunction(sum, UInt64)
|
||
-- B6 : Generic Accept ratio
|
||
count_generic_accept SimpleAggregateFunction(sum, UInt64)
|
||
-- B7 : HTTP/1.0 ratio
|
||
count_http10 SimpleAggregateFunction(sum, UInt64)
|
||
-- B8 : DF-bit variance
|
||
ip_df_variance (calculé via varPop dans la vue)
|
||
```
|
||
|
||
### Features dérivées dans `view_ai_features_1h`
|
||
|
||
| Feature | Formule | Modèle |
|
||
|---------|---------|--------|
|
||
| `ja3_diversity_ratio` | `uniqMerge(uniq_ja3) / greatest(uniq_ja4, 1)` | Complet |
|
||
| `syn_timing_cv` | `sqrt(tcp_jitter_variance) / greatest(avg_syn_ms, 1)` | Complet |
|
||
| `tls12_ratio` | `tls12_count / greatest(hits, 1)` | Complet |
|
||
| `head_ratio` | `count_head / greatest(hits, 1)` | Les deux |
|
||
| `sec_fetch_absence_rate` | `count_no_sec_fetch / greatest(hits, 1)` | Les deux |
|
||
| `generic_accept_ratio` | `count_generic_accept / greatest(hits, 1)` | Les deux |
|
||
| `http10_ratio` | `count_http10 / greatest(hits, 1)` | Les deux |
|
||
| `ip_df_variance` | `varPop(toFloat64(ip_meta_df))` | Complet |
|
||
|
||
---
|
||
|
||
# Fonctionnalités ajoutées hors propositions initiales
|
||
|
||
> Features et mécanismes implémentés dans la refonte modulaire qui ne figuraient pas
|
||
> dans les propositions A1–A10 / B1–B10 originales.
|
||
|
||
## Ensemble triple-voix (EIF + Autoencoder + XGBoost)
|
||
|
||
**Module** : `models.py`
|
||
|
||
La proposition initiale n'incluait qu'un Isolation Forest. L'architecture actuelle
|
||
utilise un **ensemble triple-voix** :
|
||
|
||
| Modèle | Poids | Rôle |
|
||
|--------|-------|------|
|
||
| Extended Isolation Forest (isotree) | 56% | Détection non supervisée principale |
|
||
| Autoencoder (PyTorch) | 24% | Erreur de reconstruction comme signal complémentaire |
|
||
| XGBoost | 20% | Supervision via feedback SOC (`soc_feedback`) |
|
||
|
||
**Formule de combinaison** :
|
||
|
||
```
|
||
final = (1 - β) × [(1 - α) × eif_norm + α × ae_norm] + β × xgb_prob
|
||
```
|
||
|
||
où `α = AE_WEIGHT = 0.30` et `β = XGB_WEIGHT = 0.20`.
|
||
|
||
## Extended Isolation Forest (isotree)
|
||
|
||
**Module** : `models.py`
|
||
|
||
Remplacement de sklearn `IsolationForest` par `isotree.ExtendedIsolationForest` :
|
||
- `ndim=min(3, len(features))` — partitions multi-dimensionnelles (contre 1D pour sklearn)
|
||
- Calibration des scores : `sklearn_equiv = 0.5 - isotree_score`
|
||
- Fallback automatique vers sklearn si `isotree` non installé
|
||
|
||
## Détection multifactorielle des navigateurs (5 axes)
|
||
|
||
**Module** : `browser.py`
|
||
|
||
Nouveau système de classification des navigateurs légitimes basé sur 5 axes
|
||
pondérés indépendants (voir `DOCUMENTATION.md` §3.4) :
|
||
|
||
| Axe | Poids | Signal |
|
||
|-----|-------|--------|
|
||
| 1 — JA4 Known | 0.25 | Famille navigateur identifiée |
|
||
| 2 — JA4 Structure | 0.15 | TLS 1.3, h2/h3, ciphers/extensions |
|
||
| 3 — HTTP Modern | 0.25 | Sec-Fetch, Accept-Language, modern_browser_score |
|
||
| 4 — Nav Behavior | 0.15 | Cookies, Referer, assets, navigation |
|
||
| 5 — TLS Coherence | 0.20 | ALPN, window scale, TLS 1.2 ratio |
|
||
|
||
Seuil : `browser_confidence ≥ 0.55` + famille identifiée → `LEGITIMATE_BROWSER`.
|
||
|
||
## Escalade de campagne
|
||
|
||
**Module** : `pipeline.py` lignes 311–324
|
||
|
||
Après le clustering HDBSCAN, les IPs appartenant à un cluster de **≥ 5 membres**
|
||
voient leur threat level escaladé de `HIGH` → `CRITICAL`. Identifie les campagnes
|
||
de botnet coordonnées.
|
||
|
||
## Feedback SOC (apprentissage supervisé)
|
||
|
||
**Module** : `cycle.py` lignes 221–238
|
||
|
||
Intégration du feedback des analystes SOC depuis la table `audit_logs` :
|
||
- Faux positifs (FP) → reclassés en baseline humaine (`asn_label='isp'`)
|
||
- Vrais positifs (TP) → étiquetés `soc_confirmed_bot` pour l'entraînement XGBoost
|
||
- Configurable via `ENABLE_FEEDBACK=true` et `FEEDBACK_WINDOW_DAYS=7`
|
||
|
||
## Features thèse §5
|
||
|
||
**Module** : `preprocessing.py` + `cycle.py`
|
||
|
||
9 features issues de la thèse (§5) enrichies depuis `view_thesis_features_1h` :
|
||
- `path_transition_entropy` — entropie des transitions entre chemins
|
||
- `cadence_cv` — coefficient de variation de la cadence
|
||
- `burst_ratio` / `pause_ratio` — ratios de rafales et pauses
|
||
- `lag1_autocorrelation` — autocorrélation lag-1 des inter-arrivées
|
||
- `benford_deviation` — déviation par rapport à la loi de Benford
|
||
- `host_diversity` / `host_sweep_speed` / `host_coverage_uniformity`
|
||
|
||
## Features P0+P1 (sous-exploitées et nouvelles)
|
||
|
||
**Module** : `preprocessing.py`
|
||
|
||
Features supplémentaires ajoutées à `FEATURES` :
|
||
- `is_fake_navigation` — détection de fausse navigation (P0)
|
||
- `true_window_size` / `window_mss_ratio` — TCP window analysis (P0, Complet)
|
||
- `has_xff` — présence de X-Forwarded-For (P1)
|
||
- `unusual_content_type_ratio` — Content-Types inhabituels (P1)
|
||
- `non_standard_port_ratio` — ports non standard (P1)
|
||
- `login_post_concentration` — concentration de POST sur login (P1)
|
||
- `sec_ch_mobile_mismatch` — incohérence Sec-CH-UA-Mobile (P1)
|
||
|
||
## TTL Fingerprinting OS
|
||
|
||
**Module** : `preprocessing.py` — `FEATURES_COMPLET`
|
||
|
||
Features TCP ajoutées pour le fingerprinting du système d'exploitation :
|
||
- `avg_ttl` — TTL moyen (différent par OS : 64 Linux, 128 Windows, 255 Cisco)
|
||
- `ttl_std` — écart-type du TTL (doit être ~0 pour un trafic homogène)
|
||
- `no_window_scale_ratio` — ratio de sessions sans TCP window scaling
|
||
|
||
## Dérive JA4 intra-session
|
||
|
||
**Module** : `preprocessing.py` — `FEATURES_COMPLET`
|
||
|
||
- `ja4_drift_ratio` — fraction de sessions où le JA4 change pendant la session.
|
||
Signal de bot qui reconfigure son client TLS à chaque requête.
|
||
|
||
## Validation gate (modèles)
|
||
|
||
**Module** : `models.py`
|
||
|
||
Après entraînement, le modèle est validé sur un jeu de validation (split 80/20).
|
||
Si `val_anomaly_rate > 0.20`, le modèle est rejeté et l'ancien modèle est conservé.
|
||
Empêche le déploiement de modèles dégénérés.
|
||
|
||
## Feature pruning automatique
|
||
|
||
**Module** : `models.py`
|
||
|
||
Les features avec variance < 1e-6 sont automatiquement éliminées avant
|
||
l'entraînement de l'EIF. Évite les partitions dégénérées sur des features constantes.
|
||
|