- README: version 2.0.0, TCP fingerprinting, clustering multi-métriques, structure fichiers mise à jour, endpoints API complets, workflows d'investigation, services techniques, tables ClickHouse, thème - RAPPORT_FINAL: section v2.0.0 avec détails TCP fingerprinting (20 signatures, scoring multi-signal, résultats production), clustering (21 features, K-means++, PCA, résultats 289 bots), redesign visualisation (tableau de bord + graphe), points d'attention Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
13 KiB
Rapport Final — SOC Bot Detector Dashboard
Date : 2026-03-16
Commits : 8032eba (corrections bugs), d4c3512 (améliorations)
1. Corrections de bugs (commit 8032eba)
| Bug | Cause | Correction |
|---|---|---|
Brute Force > Attaquants : IPs affichées en ::ffff:x.x.x.x |
Pas de normalisation IPv6 dans la requête SQL | replaceRegexpAll(toString(src_ip), '^::ffff:', '') ajouté |
| Brute Force > Cibles : lien "Voir détails" → page inexistante | Navigation vers /investigation/{host} (hostname) au lieu d'une IP |
Remplacement par composant TargetRow avec expansion inline des attaquants par host |
| Header Fingerprint : tableau de détail toujours vide | Frontend lisait data.ips au lieu de data.items |
Correction de la clé |
| Heatmap Temporelle : "Top hosts ciblés" vide | Frontend lisait data.hosts + erreur de type TypeScript { hosts: TopHost[] } |
Correction clé data.items + type annotation |
| Botnets Distribués : clic sur ligne n'affiche rien | Frontend lisait data.countries au lieu de data.items |
Correction de la clé |
Rotation & Persistance : IPs en ::ffff: + historique toujours vide |
Pas de normalisation + frontend lisait data.history au lieu de data.ja4_history |
Normalisation SQL + correction de la clé |
| TCP Spoofing : spoofings détectés sans corrélation TTL | Filtre Python-side sur données déjà filtrées TTL=30–31 | Filtre SQL spoof_only déplacé côté ClickHouse |
2. Améliorations implémentées (commit d4c3512)
J — Synthèse IP multi-sources
- Endpoint :
GET /api/investigation/{ip}/summary - Widget :
IPActivitySummaryen haut de toute page d'investigation IP - Données : ML + bruteforce + TCP spoofing + JA4 rotation + persistance + timeline 24h
- Score de risque : 0–100 (jauge SVG colorée)
- Résultat : Contexte immédiat en un coup d'œil, sans naviguer entre 6 pages
I — Comparaison baseline 24h/hier
- Endpoint :
GET /api/metrics/baseline - Widget : 3 cartes (Détections 24h, IPs uniques, CRITICAL) avec variation ▲▼ en %
- Impact : Détecte immédiatement les pics anormaux (ex: +246% détections observé)
M-4 — Score de sophistication adversaire
- Endpoint :
GET /api/rotation/sophistication - Calcul : JOIN 3 tables (rotation JA4 × 10 + récurrence × 20 + log(bruteforce+1) × 5)
- Tiers : APT-like / Advanced / Automated / Basic
- Résultat : Prioritisation des enquêtes les plus urgentes
M-7 — Chasse proactive (low-and-slow)
- Endpoint :
GET /api/rotation/proactive-hunt - Logique : IPs récurrentes avec
abs(anomaly_score) < 0.5— volent sous le radar ML - Évaluation : "Évadeur potentiel" (ratio récurrence/score > 10) ou "Persistant modéré"
- Impact : Détecte les botnets slow-and-low que le modèle ML sous-score
M-2 — Badge réputation ASN inline
- Modification : LEFT JOIN
asn_reputationdans la requête des détections - Badge : Rouge (malicious/bot/scanner), orange (proxy/vpn), vert (human)
- Limitation : La table
asn_reputationcontient 36 ASN français (ISPs légitimes) — les ASNs malveillants connus ne sont pas encore catalogués
3. Tests exhaustifs Playwright
| Page | Résultat | Notes |
|---|---|---|
| Dashboard principal | ✅ | Baseline ▲ +246.5% détections, ▲ +11.6% IPs, = CRITICAL |
| Détections | ✅ | Badge ASN affiché (null pour ASNs hors table reputation) |
| Investigation IP (162.55.94.175) | ✅ | Score 38, TCP Spoof TTL 59, JA4 Rotation 9 sig |
| Rotation > Sophistication | ✅ | APT-like: 162.55.94.175 (score 100), 46.4.81.149 (score 100) |
| Rotation > Chasse proactive | ✅ | IPs avec scores négatifs sous le radar ML |
| Brute Force > Attaquants | ✅ | IPs propres (sans ::ffff:) |
| Brute Force > Cibles | ✅ | Expansion inline des attaquants par host |
| Header Fingerprint | ✅ | Tableau détail rempli au clic |
| Heatmap Temporelle | ✅ | Top hosts ciblés affiché |
| Botnets Distribués | ✅ | Détail pays au clic |
| TCP Spoofing | ✅ | Filtre spoof_only fonctionnel |
4. Points problématiques et axes d'amélioration
🔴 Critiques
-
Table
asn_reputationincomplète — 36 entrées uniquement (ISPs français). Pour être utile, elle devrait contenir les ASNs des datacenters, VPS, proxies connus (OVH, DigitalOcean, AWS, Linode, etc.). Source suggérée : AbuseIPDB ASN database, IPInfo, Maxmind. -
Chasse proactive — scores négatifs —
view_ip_recurrence.worst_scorestocke le score brut (peut être négatif). La conditionabs(score) < 0.5capture des IPs HIGH avec score -0.18 qui sont déjà détectées par ML. Il faudrait filtrer par niveau de menace (worst_threat_level NOT IN ('HIGH', 'CRITICAL')) pour vraiment identifier les cas sous le radar. -
Pas de persistance des classifications SOC — Les classifications manuelles (
/api/analysis/classify) ne persistent que pendant la session si la tableclassificationsn'est pas créée. Un script d'init DB serait utile.
🟡 Moyens
-
Score de sophistication biaised — Les IPs avec forte rotation JA4 mais
recurrence=0dansview_ip_recurrence(non présentes) atteignent quand même score 100. Les données des deux vues ne sont pas toujours cohérentes sur la même période temporelle. -
Timeline 24h dans la synthèse IP — Utilise
window_start >= now() - INTERVAL 24 HOURsuragg_host_ip_ja4_1h. Si les données ont moins de 24h d'historique, le graphique sera partiel/vide. Adapter la fenêtre dynamiquement selon les données disponibles. -
Heatmap Temporelle — Les données de
agg_host_ip_ja4_1hne sont agrégées que pour les dernières 24h dans l'endpoint. Un sélecteur de plage temporelle (7j, 30j) permettrait de détecter les patterns de vagues cycliques (botnets hebdomadaires). -
Pas d'export des résultats — Les analystes SOC ne peuvent pas exporter les listes d'IPs malveillantes (CSV, STIX). Un endpoint
GET /api/rotation/sophistication?format=csvserait utile pour l'IOC sharing.
🟢 Mineurs
-
"Investiguer" dans le RotationView ne transmet pas le contexte — Un clic sur "Investiguer" depuis l'onglet Sophistication navigue vers
/investigation/{ip}sans pré-charger le contexte de l'onglet source. Un?source=sophistication&score=100dans l'URL permettrait d'afficher un bandeau contextuel. -
Onglets non présents dans la sidebar — Les 7 dashboards d'analyse avancée ne sont pas organisés en sous-menus. Avec l'ajout des onglets Sophistication et Chasse proactive dans Rotation, la sidebar commence à être longue.
-
Badge ASN ne trie pas les détections — Il n'y a pas encore de filtre "Afficher seulement les ASNs malveillants" dans les détections.
5. Architecture — points de vigilance
- Le SPA catch-all (
/{full_path:path}) doit rester le dernier router dansmain.py - L'endpoint
/api/investigation/{ip}/summaryutilise le préfixe/api/investigation— compatible avec la route SPA/investigation/:ip(distinct) - Les scores négatifs dans
anomaly_scoreetworst_scoresont normaux — toujours utiliserabs()pour l'affichage - Les IPv6-mapped (
::ffff:x.x.x.x) sont présentes dans toutes les vues agrégées — systématiquement utiliserreplaceRegexpAll(toString(src_ip), '^::ffff:', '')
Rapport — v2.0.0 : TCP Fingerprinting Multi-Signal + Clustering IPs
Date : 2026-03-19
Commit : e2db8ca
1. TCP Fingerprinting OS amélioré
Problème initial
L'ancien tcp_spoofing.py utilisait uniquement le TTL avec 3 plages grossières (≤64 = Linux, ≤128 = Windows, sinon = Network). Résultat : faux positifs, aucune détection de bots scanners.
Solution implémentée
backend/services/tcp_fingerprint.py — 20 signatures OS, scoring multi-signal :
| Signal | Poids | Source ClickHouse |
|---|---|---|
| TTL initial (estimé) | 40% | tcp_ttl_raw |
| MSS | 30% | tcp_mss_raw |
| Fenêtre TCP | 20% | tcp_win_raw |
| Scale factor | 10% | tcp_scale_raw |
Détections validées en production :
- Masscan :
win=5808, mss=1452, scale=4, TTL 48–57→ confiance 97% - Googlebot : stack Windows détecté avec UA Android → spoof confirmé
- Bot-tool :
risk_score += 30(vs +15 pour spoof simple)
MSS → chemin réseau :
- 1460 → Ethernet standard
- 1452 → PPPoE / DSL (Masscan pattern)
- 1420–1452 → VPN probable
- < 1420 → Tunnel / double-encapsulation
Fichiers modifiés :
backend/services/tcp_fingerprint.py(nouveau)backend/routes/tcp_spoofing.py(réécriture complète — queriesagg_host_ip_ja4_1h)backend/routes/investigation_summary.py(utilise le service tcp_fingerprint)frontend/src/components/TcpSpoofingView.tsx(nouvelles colonnes MSS/scale/confiance, graphique distribution MSS)
2. Clustering IPs multi-métriques
Problème initial
La première version du clustering utilisait uniquement des règles sur les propriétés TCP. L'utilisateur a demandé d'utiliser l'ensemble des métriques disponibles.
Solution implémentée
backend/services/clustering_engine.py — K-means++ pur Python (sans dépendances ML) :
21 features normalisées [0,1] :
| Catégorie | Features |
|---|---|
| Stack TCP (4) | TTL initial, MSS, scale, fenêtre |
| Anomalie ML (6) | score, vélocité, fuzzing, headless, POST ratio, IP-ID zéro |
| TLS/Protocole (5) | ALPN mismatch, ALPN absent, efficacité H2, ordre headers, UA-CH mismatch |
| Navigateur (1) | score navigateur moderne (normalisé /50) |
| Temporel (3) | entropie, diversité JA4 (log1p), UA rotatif |
| Comportement (2) | ratio assets, ratio accès direct |
Algorithme :
K-means++ : init O(k·n), n_init=3, meilleure inertie retenue
Power iter : X^T(Xv) trick, O(n·d) par iter — pas de matrice n×n
Déflation : Hotelling pour PC2 après extraction PC1
Stratégie d'échantillonnage : ORDER BY avg(abs(anomaly_score)) DESC → les bots (score élevé) sont inclus en priorité, même si leurs hits individuels sont faibles (cas Masscan).
Résultats en production (k=14, 3000 IPs) :
- 289 bots confirmés : clusters UA rotatif + UA-CH mismatch (cloud providers : Microsoft, Google, Akamai)
- 655 IPs suspects : anomalie ML modérée ou UA-CH incohérent
- ASN dominants : MICROSOFT-CORP-MSN-AS-BLOCK, GOOGLE-CLOUD-PLATFORM, OVH, AMAZON
- Temps de calcul : ~5–9 secondes (Python pur, 3000 points × 21 features)
3. Visualisation clustering redesignée
Problème initial
La première version utilisait des bulles ReactFlow positionnées par PCA. L'utilisateur a signalé : "l'affichage du graphe est illisible".
Solution implémentée
Deux vues distinctes, accessibles par onglets :
⊞ Tableau de bord (défaut — toujours lisible)
- Grille de cartes groupées par niveau de risque
- Bots & Menaces confirmées (rouge) → Suspects (orange) → Légitimes (vert)
- Chaque carte : label + IP count + hits + badge CRITIQUE/ÉLEVÉ/MODÉRÉ/SAIN + 4 mini-barres + stack TCP + pays + ASN
⬡ Graphe de relations
- Nœuds-cartes ReactFlow (220px — texte entièrement lisible)
- Colonnes par niveau de menace (disposition déterministe, pas PCA)
- Arêtes colorées : orange=similaire, gris=distant, animé=très fort
- Légende intégrée, minimap, contrôles zoom
Sidebar de détail
- RadarChart comportemental (10 axes)
- Toutes les métriques avec barres de progression
- Liste des IPs avec badges menace/pays
- Export Copier IPs + ⬇ CSV
- Intégrée dans le flux flex (ne bloque plus la barre de contrôle)
Fichiers modifiés :
backend/routes/clustering.py(réécriture complète)backend/services/clustering_engine.py(nouveau — seuils calibrés sur données réelles)frontend/src/components/ClusteringView.tsx(réécriture complète)frontend/src/App.tsx(route/clustering+ nav "🔬 Clustering IPs")
4. Points d'attention
Performances
- K-means++ sur 3000 × 21 : 5–9s (acceptable — pas de cache implémenté)
- Le cache mémoire du drill-down (
_cache["cluster_ips"]) est volatile : rechargement = recalcul - Pour améliorer : cache Redis ou TTL 5 min avec
functools.lru_cache
Calibration des seuils
Les seuils de name_cluster() et risk_score_from_centroid() sont calibrés sur les données observées :
anomaly_scoreen production : plage 0.2–0.35 (pas 0–1 comme attendu)- Score normalisé affiché :
min(1, score / 0.5)pour étirer la plage utile - UA-CH mismatch = 1.0 sur les clusters bot = signal très fort (cloud providers simulant un navigateur)
Données manquantes dans le LEFT JOIN
Certaines IPs n'apparaissent pas dans ml_detected_anomalies (score=0, fuzz=0). Ce sont les IPs légitimes non détectées par le modèle ML. Elles forment naturellement les clusters "Trafic Légitime".
Fuzzing_index = 100% dans beaucoup de clusters
Après analyse : le fuzzing_index log-normalisé dépasse souvent le seuil de 100% car les valeurs brutes sont très variables (0 à 229+). Ce n'est pas un bug — c'est la nature du trafic web moderne (beaucoup de requêtes avec des paths variés).