feat(bot-detector): add dynamic browser profiling engine with HDBSCAN clustering

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>
This commit is contained in:
Jacquin Antoine
2026-04-13 02:06:00 +02:00
parent 64dada980f
commit c60ce97f23
9 changed files with 1325 additions and 23 deletions

View File

@ -7,7 +7,7 @@
## Résumé
Ce document présente une architecture complète de détection et classification du trafic HTTP malveillant, positionnée à la frontière des générations 3 et 4 de défenses applicatives. Le système exploite 96 features organisées en 8 familles couvrant les couches réseau L3 à L7, corrélant des signaux TCP, TLS et HTTP en un vecteur unifié par session. La détection repose sur un ensemble triple-voix combinant un Extended Isolation Forest (EIF), un autoencodeur (AE) et XGBoost, fusionnés par un MetaLearner à régression logistique activé à partir de 1 000 étiquettes accumulées. L'explicabilité est assurée par ExIFFI (nativement pour EIF) et SHAP TreeExplainer (pour XGBoost). Le clustering de campagnes est réalisé par HDBSCAN dans l'espace latent 16 dimensions de l'autoencodeur, et la détection de flottes coordonnées par graphes bipartis via NetworkX. Le fingerprinting HTTP/2 passif — extraction des trames SETTINGS, WINDOW_UPDATE et de l'ordre des pseudo-headers côté serveur — constitue un signal inédit difficile à contourner sans implémenter une pile HTTP/2 complète fidèle à un navigateur cible. L'infrastructure repose sur 14 modules Python (3 700 lignes), une base ClickHouse à double schéma (ja4_logs bruts TTL 2 h, ja4_processing agrégés TTL 7 j), des cycles d'analyse de 300 secondes, et traite en production plus de 3 millions de logs, environ 34 000 sessions par cycle, avec approximativement 777 anomalies détectées par cycle (≈ 2,3 %). Les mots-clés couvrent : fingerprinting réseau, JA4+, HTTP/2 fingerprinting passif, détection de bots, Extended Isolation Forest, ExIFFI, autoencodeurs, méta-learner, ensemble hybride, corrélation TCP/TLS/HTTP, WAF, classification de trafic, apprentissage semi-supervisé, clustering HDBSCAN.
Ce document présente une architecture complète de détection et classification du trafic HTTP malveillant, positionnée à la frontière des générations 3 et 4 de défenses applicatives. Le système exploite 96 features organisées en 8 familles couvrant les couches réseau L3 à L7, corrélant des signaux TCP, TLS et HTTP en un vecteur unifié par session. La détection repose sur un ensemble triple-voix combinant un Extended Isolation Forest (EIF), un autoencodeur (AE) et XGBoost, fusionnés par un MetaLearner à régression logistique activé à partir de 1 000 étiquettes accumulées. L'explicabilité est assurée par ExIFFI (nativement pour EIF) et SHAP TreeExplainer (pour XGBoost). Le clustering de campagnes est réalisé par HDBSCAN dans l'espace latent 16 dimensions de l'autoencodeur, et la détection de flottes coordonnées par graphes bipartis via NetworkX. Le fingerprinting HTTP/2 passif — extraction des trames SETTINGS, WINDOW_UPDATE et de l'ordre des pseudo-headers côté serveur — constitue un signal inédit difficile à contourner sans implémenter une pile HTTP/2 complète fidèle à un navigateur cible. L'infrastructure repose sur 16 modules Python (4 800 lignes), une base ClickHouse à double schéma (ja4_logs bruts TTL 2 h, ja4_processing agrégés TTL 7 j), des cycles d'analyse de 300 secondes, et traite en production plus de 3 millions de logs, environ 34 000 sessions par cycle, avec approximativement 777 anomalies détectées par cycle (≈ 2,3 %). Le système intègre un moteur de profiling dynamique automatique des navigateurs (HDBSCAN sur les vecteurs H2 observés, centroïdes auto-appris, scoring temps réel par distance normalisée) qui s'adapte aux évolutions des piles HTTP/2 sans intervention manuelle. Les mots-clés couvrent : fingerprinting réseau, JA4+, HTTP/2 fingerprinting passif, détection de bots, Extended Isolation Forest, ExIFFI, autoencodeurs, méta-learner, ensemble hybride, corrélation TCP/TLS/HTTP, WAF, classification de trafic, apprentissage semi-supervisé, clustering HDBSCAN.
**Mots-clés** : fingerprinting réseau, JA4+, HTTP/2 fingerprinting, détection de bots, Extended Isolation Forest, ExIFFI, autoencoders, méta-learner, ensemble hybride, corrélation TCP/TLS/HTTP, WAF, classification de trafic, apprentissage semi-supervisé, clustering HDBSCAN
@ -847,14 +847,15 @@ httpcloak est un outil d'évasion qui tente d'imiter l'empreinte TLS de Chrome.
┌─────────▼─────────────────────────────┐
│ bot_detector │
│ (14 modules Python, │
3 700 lignes) │
│ (16 modules Python, │
4 800 lignes) │
│ │
│ Cycle 300s: │
│ ┌──────────────────────────────────┐ │
│ │ 1. Chargement features ClickHouse│ │
│ │ 2. dict lookups (IP, JA4, ASN) │ │
│ │ 3. browser_matcher scoring │ │
│ │ 3b. dynamic H2 profiling scoring │ │
│ │ 4. EIF bifurqué (complet/appli) │ │
│ │ 5. AE reconstruction scoring │ │
│ │ 6. XGBoost probabilité │ │
@ -1084,9 +1085,9 @@ La valeur `percentile_5` du historique des scores négatifs (anomalies confirmé
## 3.9 Browser Signature Detection (browser_matcher)
`[impl.]` **Statut global : capture H2 et scoring entièrement implémentés**
`[impl.]` **Statut global : capture H2, scoring statique et profiling dynamique entièrement implémentés**
Le système `browser_confidence` à 6 axes 3.8) fournit un score agrégé utile comme feature ML, mais sa logique de bypass (seuil 0,55) manque de précision face aux outils d'évasion modernes notamment `httpcloak`, `curl_cffi` et `python-requests` patchés qui reproduisent les couches TLS et HTTP/1.1 sans reproduire les subtilités HTTP/2. `browser_matcher` adresse cette lacune en implémentant une correspondance structurée par famille de navigateur, fondée sur l'empreinte passive du protocole HTTP/2. Le module de scoring à 7 dimensions pondérées (`browser_matcher.py`, 497 lignes) et la base de signatures Chrome/Firefox/Safari (`browser_signatures.py`, 165 lignes) sont entièrement opérationnels.
Le système `browser_confidence` à 6 axes 3.8) fournit un score agrégé utile comme feature ML, mais sa logique de bypass (seuil 0,55) manque de précision face aux outils d'évasion modernes notamment `httpcloak`, `curl_cffi` et `python-requests` patchés qui reproduisent les couches TLS et HTTP/1.1 sans reproduire les subtilités HTTP/2. `browser_matcher` adresse cette lacune en implémentant une correspondance structurée par famille de navigateur, fondée sur l'empreinte passive du protocole HTTP/2. Le module de scoring à 7 dimensions pondérées (`browser_matcher.py`, 497 lignes) et la base de signatures Chrome/Firefox/Safari (`browser_signatures.py`, 165 lignes) sont entièrement opérationnels. En complément, un moteur de **profiling dynamique automatique** (`profile_builder.py`, 614 lignes et `browser_matcher_dynamic.py`, 387 lignes) apprend les signatures des navigateurs à partir des logs réels via HDBSCAN, éliminant la dépendance aux signatures codées en dur et s'adaptant automatiquement aux nouvelles versions de navigateurs 3.9.6).
### 3.9.1 Principes du fingerprinting HTTP/2 passif
@ -1185,7 +1186,7 @@ La signature H2 de Chrome est stable depuis la version 119 (novembre 2023) jusqu
### 3.9.2 Architecture du moteur browser_matcher
Le moteur browser_matcher est structuré en deux modules Python distincts, séparant la base de données de signatures de la logique de scoring.
Le moteur browser_matcher est structuré en quatre modules Python distincts : la base de données de signatures statiques, la logique de scoring statique, le moteur de profiling dynamique hors-ligne, et le scorer dynamique temps réel.
#### Module browser_signatures.py
@ -1417,7 +1418,7 @@ Le module `browser_matcher` génère les features suivantes dans le vecteur feat
| `h2_pseudo_order` | String | Ordre observé (ex. `m,a,s,p`) | `[impl.]` (colonne `h2_pseudo_order` dans `http_logs`) |
| `tls_h2_family_mismatch` | UInt8 | 1 si JA4 dit Chrome mais H2 SETTINGS dit Firefox/outil | `[impl.]` (feature F4) |
Les features `h2_window_update_value`, `h2_has_priority_frames` et `h2_pseudo_order` sont désormais capturées par ja4ebpf via le parser HTTP/2 du flux SSL_read déchiffré et stockées dans des colonnes individuelles de `ja4_logs.http_logs`. De plus, chaque paramètre SETTINGS HTTP/2 dispose de sa propre colonne (`h2_header_table_size`, `h2_enable_push`, `h2_max_concurrent_streams`, `h2_initial_window_size`, `h2_max_frame_size`, `h2_max_header_list_size`, `h2_enable_connect_protocol`) avec la valeur -1 pour les paramètres absents du preface client. La feature `tls_h2_family_mismatch` est implémentée dans le vecteur feature global (famille F4 TLS features) et se calcule à partir des données JA4 existantes et des colonnes H2 individuelles disponibles dans `ja4_logs.http_logs`. Les features `browser_match_*` sont calculées par le module `browser_matcher.py` (497 lignes) à chaque cycle d'analyse, avec les signatures statiques de `browser_signatures.py` (165 lignes) et un rechargement dynamique optionnel depuis ClickHouse toutes les 24 heures.
Les features `h2_window_update_value`, `h2_has_priority_frames` et `h2_pseudo_order` sont désormais capturées par ja4ebpf via le parser HTTP/2 du flux SSL_read déchiffré et stockées dans des colonnes individuelles de `ja4_logs.http_logs`. De plus, chaque paramètre SETTINGS HTTP/2 dispose de sa propre colonne (`h2_header_table_size`, `h2_enable_push`, `h2_max_concurrent_streams`, `h2_initial_window_size`, `h2_max_frame_size`, `h2_max_header_list_size`, `h2_enable_connect_protocol`) avec la valeur -1 pour les paramètres absents du preface client. La feature `tls_h2_family_mismatch` est implémentée dans le vecteur feature global (famille F4 TLS features) et se calcule à partir des données JA4 existantes et des colonnes H2 individuelles disponibles dans `ja4_logs.http_logs`. Les features `browser_match_*` sont calculées par le module `browser_matcher.py` (497 lignes) à chaque cycle d'analyse, avec les signatures statiques de `browser_signatures.py` (165 lignes) et un rechargement dynamique optionnel depuis ClickHouse toutes les 24 heures. En parallèle, le module `browser_matcher_dynamic.py` (387 lignes) scores les sessions contre les profils auto-appris de `auto_browser_profiles` 3.9.6), fournissant une double couverture : signatures connues (statique) + signatures observées (dynamique).
### 3.9.5 Maintenance des signatures
@ -1468,6 +1469,100 @@ WHERE browser_match_max < 0.45
AND tls_version = 'TLS1.3';
```
### 3.9.6 Profiling dynamique automatique des navigateurs
`[impl.]` **Module profile_builder.py (614 lignes) + browser_matcher_dynamic.py (387 lignes)**
Le système de signatures statiques (§3.9.2) est robuste mais fragile face aux mises à jour de navigateurs : chaque changement de version peut modifier les valeurs SETTINGS ou WINDOW_UPDATE, nécessitant une mise à jour manuelle du dictionnaire. Le **profiling dynamique automatique** résout ce problème en apprenant les signatures directement à partir du trafic observé, sans intervention humaine.
#### Architecture du pipeline
Le pipeline s'articule en deux phases :
1. **Phase hors-ligne** (`profile_builder.py`, exécuté quotidiennement par cron) :
- Lit la vue `view_h2_profiling_raw` (sessions H2 filtrées des 7 derniers jours, dédupliquées par IP)
- Clusterise les sessions similaires via HDBSCAN (`min_cluster_size=1000`) sur un vecteur mixte (variables continues normalisées par StandardScaler + variables catégorielles brutes)
- Calcule les centroïdes par cluster : moyenne et tolérance (`mean + 3σ`) pour les continues, mode pour les catégorielles
- Labelise les clusters par analyse des User-Agents → familles `Auto_Chrome`, `Auto_Firefox`, `Auto_Safari`, `Auto_Unknown`
- Fusionne les clusters redondants (même famille + même `pseudo_order` + `window_update` à < 5% d'écart)
- Persiste les profils dans `ja4_processing.auto_browser_profiles` (ReplacingMergeTree, TTL 14 jours)
2. **Phase temps réel** (`browser_matcher_dynamic.py`, appelé à chaque cycle de 300s) :
- Charge les profils en mémoire au démarrage (rafraîchissement toutes les 24h)
- Pour chaque session, calcule un score de similarité pondéré contre tous les profils :
```
score = Σ (poids_i × similarité_i) × confiance_volumétrique
Pondération :
h2_window_update : 0.40 (le plus discriminant entre familles)
pseudo_order : 0.40 (invariant par version mineure)
h2_initial_window_size : 0.10
h2_has_priority : 0.10
Similarité continue : sim = max(0, 1 - |x - μ| / max(|μ|, 1))
Similarité catégorielle : sim = 1.0 si match exact, 0.0 sinon
Confiance volumétrique : conf = min(1.0, log10(count_ips + 1) / 4)
→ count_ips=10000 → confiance≈1.0 ; count_ips=100 → confiance≈0.50
```
#### Rejet rapide
Le scoring intègre deux mécanismes de rejet rapide pour éviter les calculs inutiles :
1. **Incompatibilité pseudo_order** : si la session a `pseudo_order = mpsa` (curl) mais le profil exige `masp` (Chrome), le score est immédiatement 0. Ce signal est binaire et invariant il ne peut pas être contourné sans reproduire exactement l'ordre des pseudo-headers du navigateur cible.
2. **Dépassement de tolérance** : si `|h2_window_update_session - h2_window_update_profil| > tolérance`, le score est 0. La tolérance est calculée comme `|μ| + 3σ` lors du clustering, garantissant un intervalle de confiance à 99.7%.
#### Vue ClickHouse d'extraction
La vue `view_h2_profiling_raw` (créée dans `13_h2_profiling.sql`) filtre le trafic bots évident et encode les variables catégorielles :
```sql
-- Encodage pseudo_order → UInt8
multiIf(
h2_pseudo_order = 'm,a,s,p', 1, -- Chrome/Safari
h2_pseudo_order = 'm,p,a,s', 2, -- Firefox (ancien)
h2_pseudo_order = 'm,s,p,a', 3,
h2_pseudo_order = 'm,p,s,a', 4, -- curl/Firefox
h2_pseudo_order = 'm,a,p,s', 5,
0 -- inconnu
) AS pseudo_order_id
```
Filtres appliqués : `h2_pseudo_order != ''` (HTTP/2 uniquement), `h2_window_update > 0` (exclut curl), `h2_initial_window_size NOT IN (-1, 65535)` (exclut absents et valeur typique curl), `header_user_agent != ''` (exclut scanners).
#### Cycle de vie des profils
Le profilage dynamique gère automatiquement l'obsolescence :
- **Rafraîchissement** : `last_seen_date` est mis à jour quotidiennement pour les profils dont le vecteur H2 correspond à des sessions observées dans les dernières 24h.
- **Purge** : les profils `last_seen_date < today() - 14` sont supprimés (navigateurs dépréciés, versions obsolètes). Cette TTL garantit que seules les signatures récentes sont utilisées pour le scoring.
Le TTL ClickHouse de 14 jours sur la table `auto_browser_profiles` assure une purge physique des partitions expirées lors du merge.
#### Fusion des clusters
La fusion des clusters redondants est critique pour éviter la prolifération de profils quasi-identiques. Deux clusters sont fusionnés si **les trois** conditions sont réunies :
1. Même `detected_family` (ex: `Auto_Chrome`)
2. Même `pseudo_order_mode` (même ordre des pseudo-headers)
3. `|window_update_A - window_update_B| / max(A, B) < 5%` (valeurs proches)
La fusion calcule la **moyenne pondérée** par `count_ips` :
```
nouvelle_moyenne = (mean_A × count_A + mean_B × count_B) / (count_A + count_B)
nouvelle_tolérance = max(tol_A, tol_B) # on élargit le seuil
```
#### Avantage sur les signatures statiques
Le profiling dynamique présente trois avantages décisifs :
1. **Adaptabilité** : les nouvelles versions de navigateurs sont automatiquement apprises dès qu'elles atteignent un volume suffisant (`min_cluster_size=1000` IPs uniques), sans mise à jour manuelle du dictionnaire.
2. **Robustesse** : la tolérance `mean + 3σ` s'adapte à la variance réelle observée dans la population, contrairement à la tolérance fixe de 1000 du système statique.
3. **Couverture** : les familles `Auto_Unknown` capturent les clients H2 qui ne correspondent à aucune famille connue mais qui présentent un comportement de navigateur légitime (nouvelles implémentations, clients exotiques).
---
@ -2933,6 +3028,8 @@ Le fingerprinting réseau opère sans déchiffrement TLS (les métadonnées TLS
| AE inference | ~50 ms | Batch de 34 000 sessions |
| XGBoost inference | ~30 ms | Batch de 34 000 sessions |
| HDBSCAN (anomalies) | ~100 ms | ~34 000 sessions, espace latent AE |
| HDBSCAN (profiling) | ~25 s | Quotidien, ~200k sessions H2 dédupliquées, min_cluster=1000 |
| Dynamic matcher scoring | < 1 ms | Par session, lookup en mémoire contre ~510 profils |
| Louvain (fleet.py) | ~50 ms | Graphe JA4×ASN, communautés |
| MetaLearner LR | < 10 ms | Régression logistique, négligeable |
| **Cycle complet** | **~300 secondes** | EIF + AE + XGBoost + HDBSCAN + Louvain |
@ -2989,7 +3086,8 @@ Un seul utilisateur réel alterne quelques connexions (26 ports source actifs
**browser_matcher maintenance (§3.9)** :
- État actuel : `[impl.]` — logique de score complète à 7 dimensions, base de signatures Chrome/Firefox/Safari complète
- Données H2 brutes : `[impl.]` — capture des 7 paramètres SETTINGS individuels, WINDOW_UPDATE, flag PRIORITY et ordre pseudo-headers par ja4ebpf via le parser HTTP/2 du flux SSL_read déchiffré. Colonnes individuelles dans `ja4_logs.http_logs` (`h2_header_table_size`, `h2_enable_push`, `h2_max_concurrent_streams`, `h2_initial_window_size`, `h2_max_frame_size`, `h2_max_header_list_size`, `h2_enable_connect_protocol`, `h2_window_update`, `h2_has_priority`, `h2_pseudo_order`)
- Travail restant : maintenance des signatures lors des nouvelles versions de navigateurs, enrichissement de la base via la file d'examen `unknown_h2_fingerprints`
- Profiling dynamique : `[impl.]` — moteur HDBSCAN hors-ligne (`profile_builder.py`, 614 lignes) + scorer temps réel (`browser_matcher_dynamic.py`, 387 lignes). Les profils auto-appris dans `auto_browser_profiles` s'adaptent automatiquement aux nouvelles versions de navigateurs, éliminant la maintenance manuelle du dictionnaire statique.
- Travail restant : monitoring de la convergence des clusters dynamiques, validation croisée entre scores statique et dynamique
**DNS Shadow Analysis (§5.6)** :
- État actuel : `[todo]` non implémenté
@ -3061,6 +3159,8 @@ La capture passive est réalisée par ja4ebpf via un uprobe sur `SSL_read` (Open
Cette technique permet de détecter des outils d'évasion qui reproduisent correctement la couche TLS (curl_cffi, httpcloak) mais échouent à reproduire les subtilités H2 — notamment l'ordre des pseudo-headers et la valeur WINDOW_UPDATE. Le module de scoring est entièrement implémenté (`browser_matcher.py`, 497 lignes) avec les signatures des trois familles majeures (Chrome, Firefox, Safari) et trois signatures non-navigateur (curl, python-httpx, Go net/http) dans `browser_signatures.py` (165 lignes).
Le **profiling dynamique automatique** (`profile_builder.py`, 614 lignes + `browser_matcher_dynamic.py`, 387 lignes) étend cette approche en apprenant les signatures H2 directement à partir du trafic observé via HDBSCAN, sans nécessiter de mise à jour manuelle du dictionnaire. Les centroïdes auto-appris (moyenne + tolérance 3σ pour les continues, mode pour les catégorielles) sont stockés dans `auto_browser_profiles` et scorés en temps réel par distance normalisée pondérée avec confiance volumétrique. Ce mécanisme élimine la fragilité du système statique face aux mises à jour de navigateurs et couvre les implémentations H2 non répertoriées via les familles `Auto_Unknown`.
La stabilité des empreintes H2 de Chrome sur 2+ ans (novembre 2023 2026) contraste favorablement avec JA4 TLS (instable à chaque mise à jour de ciphersuites), justifiant l'investissement dans cette dimension additionnelle.
#### Contribution 4 : Huit techniques originales de détection
@ -3100,7 +3200,7 @@ Architecture de données fondée sur ClickHouse avec **AggregatingMergeTree view
### Perspective
Le système atteint ses objectifs opérationnels actuels. La capture HTTP/2 passive est intégrée avec 12 colonnes individuelles dans `ja4_logs.http_logs`, et le module `browser_matcher` est opérationnel avec ses 7 dimensions de scoring. Les axes d'amélioration prioritaires sont l'enrichissement continu des signatures navigateur via la file d'examen `unknown_h2_fingerprints`, l'extension DNS Shadow Analysis pour la couverture DNS (`[todo]``[partiel]`), et le passage à l'apprentissage en ligne pour XGBoost. À plus long terme, le support HTTP/3 (QUIC) deviendra nécessaire à mesure que la proportion de trafic HTTP/3 augmente dans la baseline.
Le système atteint ses objectifs opérationnels actuels. La capture HTTP/2 passive est intégrée avec 12 colonnes individuelles dans `ja4_logs.http_logs`, et le module `browser_matcher` est opérationnel avec ses 7 dimensions de scoring statique. Le moteur de profiling dynamique automatique (§3.9.6) complète le système statique en apprenant les signatures H2 à partir du trafic réel, éliminant la dépendance aux signatures codées en dur. Les axes d'amélioration prioritaires sont le monitoring de la convergence des clusters dynamiques, l'extension DNS Shadow Analysis pour la couverture DNS (`[todo]``[partiel]`), et le passage à l'apprentissage en ligne pour XGBoost. À plus long terme, le support HTTP/3 (QUIC) deviendra nécessaire à mesure que la proportion de trafic HTTP/3 augmente dans la baseline.
La technique la plus prometteuse parmi les travaux futurs est le **PARD-SSM** (Hiremath et al., 2026 [Référence à vérifier / Identifier le vrai papier]), qui permettrait de modéliser explicitement les phases d'attaque séquentielles — comblant la lacune actuelle entre la détection de sessions individuelles et la détection de campagnes d'attaque coordonnées multi-phases.