docs: mise à jour README + RAPPORT_FINAL v2.0.0
- 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>
This commit is contained in:
134
RAPPORT_FINAL.md
134
RAPPORT_FINAL.md
@ -106,3 +106,137 @@
|
|||||||
- Les **scores négatifs** dans `anomaly_score` et `worst_score` sont normaux — toujours utiliser `abs()` pour l'affichage
|
- Les **scores négatifs** dans `anomaly_score` et `worst_score` sont normaux — toujours utiliser `abs()` pour l'affichage
|
||||||
- Les **IPv6-mapped** (`::ffff:x.x.x.x`) sont présentes dans toutes les vues agrégées — systématiquement utiliser `replaceRegexpAll(toString(src_ip), '^::ffff:', '')`
|
- Les **IPv6-mapped** (`::ffff:x.x.x.x`) sont présentes dans toutes les vues agrégées — systématiquement utiliser `replaceRegexpAll(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 — queries `agg_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_score` en 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).
|
||||||
|
|||||||
325
README.md
325
README.md
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
Dashboard web interactif pour visualiser et investiguer les décisions de classification du Bot Detector IA.
|
Dashboard web interactif pour visualiser et investiguer les décisions de classification du Bot Detector IA.
|
||||||
|
|
||||||
**Version:** 1.7.0 - Subnet Investigation + IP Reputation
|
**Version:** 2.0.0 - TCP Fingerprinting Multi-Signal + Clustering IPs Multi-Métriques
|
||||||
|
|
||||||
## 🚀 Démarrage Rapide
|
## 🚀 Démarrage Rapide
|
||||||
|
|
||||||
@ -51,39 +51,63 @@ docker compose logs -f dashboard_web
|
|||||||
|
|
||||||
### Dashboard Principal
|
### Dashboard Principal
|
||||||
- **Métriques en temps réel** : Total détections, menaces, bots connus, IPs uniques
|
- **Métriques en temps réel** : Total détections, menaces, bots connus, IPs uniques
|
||||||
|
- **Comparaison baseline J-1** : variation ▲▼ vs hier (détections, IPs uniques, CRITICAL)
|
||||||
- **Répartition par menace** : Visualisation CRITICAL/HIGH/MEDIUM/LOW
|
- **Répartition par menace** : Visualisation CRITICAL/HIGH/MEDIUM/LOW
|
||||||
- **Évolution temporelle** : Graphique des détections sur 24h
|
- **Évolution temporelle** : Graphique des détections sur 24h
|
||||||
- **Incidents clusterisés** : Regroupement automatique par subnet /24
|
- **Incidents clusterisés** : Regroupement automatique par subnet /24
|
||||||
- **Top Menaces Actives** : Top 10 des IPs les plus dangereuses
|
- **Top Menaces Actives** : Top 10 des IPs les plus dangereuses
|
||||||
|
|
||||||
### Investigation Subnet /24 (NOUVEAU)
|
### 🧬 TCP Spoofing & Fingerprinting OS (amélioré v2.0)
|
||||||
- **URL:** `/entities/subnet/x.x.x.x_24` (ex: `/entities/subnet/141.98.11.0_24`)
|
- **Détection multi-signal** : TTL initial + MSS + scale + fenêtre TCP (p0f-style)
|
||||||
- **Stats globales** : Total IPs, détections, JA4 uniques, User-Agents uniques, Hosts
|
- **20 signatures OS** : Linux, Windows, macOS, Android, iOS, Masscan, ZMap, Shodan, Googlebot…
|
||||||
- **Tableau des IPs** : Toutes les IPs du subnet avec leurs statistiques
|
- **Estimation hop-count** : différence TTL initial (arrondi) − TTL observé
|
||||||
- **Actions par IP** : Investiguer, Voir détails
|
- **Détection réseau** : MSS → Ethernet (1460) / PPPoE (1452) / VPN (1420) / Tunnel (<1420)
|
||||||
- **Sources** : `ml_detected_anomalies` (détections) + `view_dashboard_entities` (user-agents)
|
- **Confiance 0–100%** : score pondéré (TTL 40% + MSS 30% + fenêtre 20% + scale 10%)
|
||||||
|
- **Badge bot-tool** : Masscan détecté à 97% (win=5808, mss=1452, scale=4)
|
||||||
|
- **Distribution MSS** : histogramme des MSS observés par cluster
|
||||||
|
|
||||||
### Investigation IP + Réputation (NOUVEAU)
|
### 🔬 Clustering IPs Multi-Métriques (nouveau v2.0)
|
||||||
|
- **URL:** `/clustering`
|
||||||
|
- **Algorithme :** K-means++ (Arthur & Vassilvitskii, 2007), initialisé avec k-means++, 3 runs
|
||||||
|
- **21 features normalisées [0,1] :**
|
||||||
|
- Stack TCP : TTL initial, MSS, scale, fenêtre TCP
|
||||||
|
- Anomalie ML : score, vélocité, fuzzing, headless, POST ratio, IP-ID zéro
|
||||||
|
- TLS/Protocole : ALPN mismatch, ALPN absent, efficacité H2 (multiplexing)
|
||||||
|
- Navigateur : score navigateur moderne, ordre headers, UA-CH mismatch
|
||||||
|
- Temporel : entropie, diversité JA4, UA rotatif
|
||||||
|
- **Positionnement 2D :** PCA par puissance itérative (Hotelling) + déflation
|
||||||
|
- **Nommage automatique :** Masscan / Bot UA Rotatif / Bot Fuzzer / Anomalie ML / Linux / Windows / VPN
|
||||||
|
|
||||||
|
**Vue Tableau de bord (défaut) :**
|
||||||
|
- Grille de cartes groupées : Bots confirmés → Suspects → Légitimes
|
||||||
|
- Chaque carte : label, IP count, hits, badge CRITIQUE/ÉLEVÉ/MODÉRÉ/SAIN
|
||||||
|
- 4 mini-barres : anomalie, UA-CH mismatch, fuzzing, UA rotatif
|
||||||
|
- Stack TCP (TTL, MSS, Scale), top pays, ASN
|
||||||
|
|
||||||
|
**Vue Graphe de relations :**
|
||||||
|
- Nœuds-cartes ReactFlow (220px, texte lisible)
|
||||||
|
- Colonnes par niveau de menace : Bots | Suspects | Légitimes
|
||||||
|
- Arêtes colorées par similarité (orange=fort, animé=très fort)
|
||||||
|
- Légende intégrée, minimap, contrôles zoom
|
||||||
|
|
||||||
|
**Sidebar de détail :**
|
||||||
|
- RadarChart comportemental (10 axes : anomalie, UA-CH, fuzzing, headless…)
|
||||||
|
- Toutes les métriques avec barres de progression colorées
|
||||||
|
- Liste des IPs avec badges menace/pays/ASN
|
||||||
|
- Export **Copier IPs** + **⬇ CSV**
|
||||||
|
|
||||||
|
### Investigation Subnet /24
|
||||||
|
- **URL:** `/entities/subnet/x.x.x.x_24`
|
||||||
|
- Stats globales, tableau des IPs, actions par IP
|
||||||
|
|
||||||
|
### Investigation IP + Réputation
|
||||||
- **URL:** `/investigation/:ip`
|
- **URL:** `/investigation/:ip`
|
||||||
- **Panel Réputation IP** :
|
- Synthèse multi-sources (ML + bruteforce + TCP + JA4 + timeline)
|
||||||
- Score de menace 0-100
|
- Score de risque 0–100, réputation IP-API + IPinfo
|
||||||
- Niveau: clean/low/medium/high/critical
|
|
||||||
- Détection: Proxy, Hosting, VPN, Tor
|
|
||||||
- Géolocalisation: Pays, Ville
|
|
||||||
- ASN + Organisation
|
|
||||||
- **Sources** : IP-API.com + IPinfo.io (sans clé API)
|
|
||||||
|
|
||||||
### Investigation (Variabilité)
|
### Investigation (Variabilité)
|
||||||
- **Vue détails** : Cliquez sur une IP/JA4/pays/ASN pour investiguer
|
- User-Agents, JA4 fingerprints, pays, ASN, hosts, niveaux de menace
|
||||||
- **Variabilité des attributs** :
|
- Insights automatiques, navigation enchaînable
|
||||||
- User-Agents associés (avec pourcentages)
|
|
||||||
- JA4 fingerprints
|
|
||||||
- Pays de provenance
|
|
||||||
- ASN
|
|
||||||
- Hosts contactés
|
|
||||||
- Niveaux de menace
|
|
||||||
- **Insights automatiques** : Détection de comportements suspects
|
|
||||||
- **Navigation enchaînable** : Cliquez sur un attribut pour creuser
|
|
||||||
|
|
||||||
## 🏗️ Architecture
|
## 🏗️ Architecture
|
||||||
|
|
||||||
@ -92,51 +116,67 @@ docker compose logs -f dashboard_web
|
|||||||
│ Docker Compose │
|
│ Docker Compose │
|
||||||
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────┐ │
|
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────┐ │
|
||||||
│ │ ClickHouse │ │ bot_detector│ │ dashboard_web │ │
|
│ │ ClickHouse │ │ bot_detector│ │ dashboard_web │ │
|
||||||
│ │ :8123 │ │ (existant) │ │ :3000 (web) │ │
|
│ │ :8123 │ │ (existant) │ │ :8000 (web+API)│ │
|
||||||
│ │ :9000 │ │ │ │ :8000 (API) │ │
|
│ │ :9000 │ │ │ │ network=host │ │
|
||||||
│ └──────┬──────┘ └──────┬──────┘ └────────┬────────┘ │
|
│ └──────┬──────┘ └──────┬──────┘ └────────┬────────┘ │
|
||||||
│ └────────────────┴───────────────────┘ │
|
│ └────────────────┴───────────────────┘ │
|
||||||
└─────────────────────────────────────────────────────────┘
|
└─────────────────────────────────────────────────────────┘
|
||||||
```
|
```
|
||||||
|
|
||||||
|
> Le container utilise `network_mode: "host"` — le frontend buildé est servi par FastAPI
|
||||||
|
> sur le **port 8000 uniquement** (pas de port 3000 en production).
|
||||||
|
|
||||||
### Composants
|
### Composants
|
||||||
|
|
||||||
| Composant | Technologie | Port | Description |
|
| Composant | Technologie | Description |
|
||||||
|-----------|-------------|------|-------------|
|
|-----------|-------------|-------------|
|
||||||
| **Frontend** | React + TypeScript + Tailwind | 3000 | Interface utilisateur |
|
| **Frontend** | React 18 + TypeScript 5 + Vite 5 + Tailwind CSS 3 | Interface utilisateur (SPA) |
|
||||||
| **Backend API** | FastAPI (Python) | 8000 | API REST |
|
| **Backend API** | FastAPI 0.111 + Python 3.11 | API REST + serveur statique SPA |
|
||||||
| **Database** | ClickHouse (existant) | 8123 | Base de données |
|
| **Database** | ClickHouse (existant) — port 8123 | Base de données principale |
|
||||||
|
| **Clustering** | K-means++ pur Python + PCA puissance itérative | Algorithmes embarqués, sans dépendance ML |
|
||||||
|
|
||||||
## 📁 Structure
|
## 📁 Structure
|
||||||
|
|
||||||
```
|
```
|
||||||
dashboard/
|
dashboard/
|
||||||
├── Dockerfile # Image Docker multi-stage
|
├── Dockerfile # Multi-stage: node:20-alpine → python:3.11-slim
|
||||||
├── requirements.txt # Dépendances Python
|
├── docker-compose.yaml
|
||||||
|
├── requirements.txt
|
||||||
├── backend/
|
├── backend/
|
||||||
│ ├── main.py # Application FastAPI
|
│ ├── main.py # FastAPI: CORS, routers, SPA catch-all (doit être DERNIER)
|
||||||
│ ├── config.py # Configuration
|
│ ├── config.py # pydantic-settings, lit .env
|
||||||
│ ├── database.py # Connexion ClickHouse
|
│ ├── database.py # ClickHouseClient singleton (db)
|
||||||
│ ├── models.py # Modèles Pydantic
|
│ ├── models.py # Modèles Pydantic v2
|
||||||
│ └── routes/
|
│ ├── routes/
|
||||||
│ ├── metrics.py # Endpoint /api/metrics
|
│ │ ├── metrics.py # GET /api/metrics, /api/metrics/baseline
|
||||||
│ ├── detections.py # Endpoint /api/detections
|
│ │ ├── detections.py # GET /api/detections
|
||||||
│ ├── variability.py # Endpoint /api/variability
|
│ │ ├── variability.py # GET /api/variability
|
||||||
│ └── attributes.py # Endpoint /api/attributes
|
│ │ ├── attributes.py # GET /api/attributes
|
||||||
|
│ │ ├── incidents.py # GET /api/incidents/clusters
|
||||||
|
│ │ ├── entities.py # GET /api/entities
|
||||||
|
│ │ ├── analysis.py # GET/POST /api/analysis — classifications SOC
|
||||||
|
│ │ ├── reputation.py # GET /api/reputation/ip/{ip}
|
||||||
|
│ │ ├── tcp_spoofing.py # GET /api/tcp-spoofing — fingerprinting OS multi-signal
|
||||||
|
│ │ ├── clustering.py # GET /api/clustering/clusters + /cluster/{id}/ips
|
||||||
|
│ │ └── investigation_summary.py # GET /api/investigation/{ip}/summary
|
||||||
|
│ └── services/
|
||||||
|
│ ├── tcp_fingerprint.py # 20 signatures OS, scoring, hop-count, réseau path
|
||||||
|
│ ├── clustering_engine.py # K-means++, PCA-2D, nommage, score risque (pur Python)
|
||||||
|
│ └── reputation_ip.py # httpx → ip-api.com + ipinfo.io (async, sans API key)
|
||||||
└── frontend/
|
└── frontend/
|
||||||
├── package.json # Dépendances Node
|
├── package.json
|
||||||
├── src/
|
├── vite.config.ts # Proxy /api → :8000 en dev
|
||||||
│ ├── App.tsx # Composant principal
|
└── src/
|
||||||
│ ├── components/
|
├── App.tsx # BrowserRouter + Sidebar + TopHeader + Routes
|
||||||
│ │ ├── DetectionsList.tsx
|
├── ThemeContext.tsx # dark/light/auto, localStorage: soc_theme
|
||||||
│ │ ├── DetailsView.tsx
|
├── api/client.ts # Axios baseURL=/api + toutes les interfaces TypeScript
|
||||||
│ │ └── VariabilityPanel.tsx
|
├── components/
|
||||||
│ ├── hooks/
|
│ ├── ClusteringView.tsx # K-means++ clustering — 2 vues
|
||||||
│ │ ├── useMetrics.ts
|
│ ├── TcpSpoofingView.tsx # TCP fingerprinting OS
|
||||||
│ │ ├── useDetections.ts
|
│ ├── InvestigationView.tsx # Investigation IP complète
|
||||||
│ │ └── useVariability.ts
|
│ └── ... # Autres vues
|
||||||
│ └── api/
|
├── hooks/ # useMetrics, useDetections, useVariability (polling)
|
||||||
│ └── client.ts # Client API
|
└── utils/STIXExporter.ts
|
||||||
```
|
```
|
||||||
|
|
||||||
## 🔌 API
|
## 🔌 API
|
||||||
@ -146,46 +186,51 @@ dashboard/
|
|||||||
| Method | Endpoint | Description |
|
| Method | Endpoint | Description |
|
||||||
|--------|----------|-------------|
|
|--------|----------|-------------|
|
||||||
| GET | `/api/metrics` | Métriques globales |
|
| GET | `/api/metrics` | Métriques globales |
|
||||||
|
| GET | `/api/metrics/baseline` | Comparaison J-1 (détections, IPs, CRITICAL) |
|
||||||
| GET | `/api/metrics/threats` | Distribution par menace |
|
| GET | `/api/metrics/threats` | Distribution par menace |
|
||||||
| GET | `/api/detections` | Liste des détections |
|
| GET | `/api/detections` | Liste des détections paginée |
|
||||||
| GET | `/api/detections/{id}` | Détails d'une détection |
|
| GET | `/api/detections/{id}` | Détails d'une détection |
|
||||||
| GET | `/api/variability/{type}/{value}` | Variabilité d'un attribut |
|
| GET | `/api/variability/{type}/{value}` | Variabilité d'un attribut |
|
||||||
| GET | `/api/attributes/{type}` | Liste des valeurs uniques |
|
| GET | `/api/attributes/{type}` | Valeurs uniques d'un attribut |
|
||||||
| GET | `/api/incidents/clusters` | Incidents clusterisés par subnet /24 |
|
| GET | `/api/incidents/clusters` | Incidents clusterisés par subnet /24 |
|
||||||
| GET | `/api/entities/subnet/{subnet}` | Investigation subnet (ex: `141.98.11.0/24`) |
|
| GET | `/api/entities/subnet/{subnet}` | Investigation subnet (ex: `141.98.11.0_24`) |
|
||||||
| GET | `/api/entities/{type}/{value}` | Investigation entité (IP, JA4, UA, etc.) |
|
| GET | `/api/entities/{type}/{value}` | Investigation entité (IP, JA4, UA…) |
|
||||||
| GET | `/api/reputation/ip/{ip}` | Réputation IP (IP-API + IPinfo) |
|
| GET | `/api/reputation/ip/{ip}` | Réputation IP (IP-API + IPinfo) |
|
||||||
|
| GET | `/api/investigation/{ip}/summary` | Synthèse IP multi-sources (ML + TCP + JA4) |
|
||||||
| GET | `/api/analysis/{ip}/subnet` | Analyse subnet / ASN |
|
| GET | `/api/analysis/{ip}/subnet` | Analyse subnet / ASN |
|
||||||
| GET | `/api/analysis/{ip}/country` | Analyse pays |
|
|
||||||
| GET | `/api/analysis/{ip}/ja4` | Analyse JA4 |
|
|
||||||
| GET | `/api/analysis/{ip}/user-agents` | Analyse User-Agents |
|
|
||||||
| GET | `/api/analysis/{ip}/recommendation` | Recommandation de classification |
|
| GET | `/api/analysis/{ip}/recommendation` | Recommandation de classification |
|
||||||
| POST | `/api/analysis/classifications` | Sauvegarder classification SOC |
|
| POST | `/api/analysis/classifications` | Sauvegarder classification SOC |
|
||||||
|
| GET | `/api/tcp-spoofing/overview` | Vue d'ensemble TCP spoofing + OS |
|
||||||
|
| GET | `/api/tcp-spoofing/list` | Liste des détections TCP spoofing |
|
||||||
|
| GET | `/api/tcp-spoofing/matrix` | Matrice OS déclaré vs OS réel |
|
||||||
|
| GET | `/api/clustering/clusters` | Clustering K-means++ (`?k=14&n_samples=3000`) |
|
||||||
|
| GET | `/api/clustering/cluster/{id}/ips` | IPs d'un cluster (drill-down) |
|
||||||
| GET | `/health` | Health check |
|
| GET | `/health` | Health check |
|
||||||
|
|
||||||
### Exemples
|
### Exemples
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Métriques globales
|
# Health check
|
||||||
curl http://localhost:3000/api/metrics
|
curl http://localhost:8000/health
|
||||||
|
|
||||||
# Détections avec filtres
|
# Métriques globales + baseline
|
||||||
curl "http://localhost:3000/api/detections?threat_level=CRITICAL&page=1"
|
curl http://localhost:8000/api/metrics | jq '.summary'
|
||||||
|
curl http://localhost:8000/api/metrics/baseline | jq
|
||||||
|
|
||||||
# Variabilité d'une IP
|
# Détections CRITICAL
|
||||||
curl http://localhost:3000/api/variability/ip/192.168.1.100
|
curl "http://localhost:8000/api/detections?threat_level=CRITICAL&page=1" | jq '.items | length'
|
||||||
|
|
||||||
# Investigation subnet (URL encode / en _24)
|
# TCP Spoofing — vue d'ensemble
|
||||||
curl "http://localhost:3000/api/entities/subnet/141.98.11.0_24?hours=24"
|
curl http://localhost:8000/api/tcp-spoofing/overview | jq
|
||||||
|
|
||||||
|
# Clustering IPs (14 clusters sur 3000 échantillons)
|
||||||
|
curl "http://localhost:8000/api/clustering/clusters?k=14&n_samples=3000" | jq '.stats'
|
||||||
|
|
||||||
|
# Drill-down d'un cluster
|
||||||
|
curl "http://localhost:8000/api/clustering/cluster/c0_k14/ips?limit=20" | jq '.ips[].ip'
|
||||||
|
|
||||||
# Réputation IP
|
# Réputation IP
|
||||||
curl http://localhost:3000/api/reputation/ip/141.98.11.209
|
curl http://localhost:8000/api/reputation/ip/162.55.94.175 | jq
|
||||||
|
|
||||||
# Incidents clusterisés
|
|
||||||
curl http://localhost:3000/api/incidents/clusters?limit=20
|
|
||||||
|
|
||||||
# Liste des pays
|
|
||||||
curl http://localhost:3000/api/attributes/country
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## ⚙️ Configuration
|
## ⚙️ Configuration
|
||||||
@ -195,39 +240,127 @@ curl http://localhost:3000/api/attributes/country
|
|||||||
| Variable | Défaut | Description |
|
| Variable | Défaut | Description |
|
||||||
|----------|--------|-------------|
|
|----------|--------|-------------|
|
||||||
| `CLICKHOUSE_HOST` | `clickhouse` | Hôte ClickHouse |
|
| `CLICKHOUSE_HOST` | `clickhouse` | Hôte ClickHouse |
|
||||||
|
| `CLICKHOUSE_PORT` | `8123` | Port HTTP ClickHouse |
|
||||||
| `CLICKHOUSE_DB` | `mabase_prod` | Base de données |
|
| `CLICKHOUSE_DB` | `mabase_prod` | Base de données |
|
||||||
| `CLICKHOUSE_USER` | `admin` | Utilisateur |
|
| `CLICKHOUSE_USER` | `admin` | Utilisateur |
|
||||||
| `CLICKHOUSE_PASSWORD` | `` | Mot de passe |
|
| `CLICKHOUSE_PASSWORD` | `` | Mot de passe |
|
||||||
| `API_PORT` | `8000` | Port de l'API |
|
| `API_HOST` | `0.0.0.0` | Bind Uvicorn |
|
||||||
|
| `API_PORT` | `8000` | Port API + frontend |
|
||||||
|
| `CORS_ORIGINS` | `["http://localhost:3000", ...]` | Origines CORS autorisées |
|
||||||
|
|
||||||
Ces variables sont lues depuis le fichier `.env` à la racine du projet.
|
Ces variables sont lues depuis le fichier `.env` à la racine du projet.
|
||||||
|
|
||||||
|
> ⚠️ Le fichier `.env` contient les credentials réels — ne jamais le committer.
|
||||||
|
|
||||||
## 🔍 Workflows d'Investigation
|
## 🔍 Workflows d'Investigation
|
||||||
|
|
||||||
### Exemple 1: Investiguer une IP suspecte
|
### Exemple 1 : Identifier un bot Masscan
|
||||||
|
|
||||||
1. **Dashboard** → Voir une IP classifiée 🔴 CRITICAL
|
1. **🔬 Clustering IPs** → Cluster "🤖 Masscan / Scanner IP" visible en rouge
|
||||||
2. **Clic sur l'IP** → Ouvre la vue détails
|
2. **Clic sur la carte** → Sidebar : TTL=52, MSS=1452, Scale=4 — pattern Masscan
|
||||||
3. **Observer User-Agents** → 3 UA différents détectés
|
3. **Copier les IPs** → Liste prête pour le blocage
|
||||||
4. **Clic sur "python-requests"** → Voir toutes les IPs avec cet UA
|
4. **Export CSV** → Import dans le SIEM ou firewall
|
||||||
5. **Découvrir 12 IPs** → Possible botnet
|
|
||||||
6. **Action** → Noter pour blacklist
|
|
||||||
|
|
||||||
### Exemple 2: Analyser un ASN
|
### Exemple 2 : Analyser des bots UA-rotatifs (cloud)
|
||||||
|
|
||||||
1. **Filtre** → ASN: OVH (AS16276)
|
1. **Clustering** → Cluster "🤖 Bot UA Rotatif + CH Mismatch" (risque 50%)
|
||||||
2. **Voir 523 détections** → Beaucoup d'activité
|
2. **RadarChart** → UA-CH=100%, UA rotatif=100%, anomalie=59%
|
||||||
3. **Variabilité** → 89 IPs différentes, 15 pays
|
3. **Top ASN** → Microsoft, Google, Akamai — cloud providers
|
||||||
4. **Insight** → "ASN de type hosting → Souvent utilisé pour des bots"
|
4. **🧬 TCP Spoofing** → Confirmer : ces IPs déclarent Windows UA mais ont TTL Linux
|
||||||
5. **Conclusion** → Activité normale pour un hébergeur
|
5. **Investigation IP** → Détail complet avec timeline 24h
|
||||||
|
|
||||||
|
### Exemple 3 : Détecter le spoofing d'OS
|
||||||
|
|
||||||
|
1. **🧬 TCP Spoofing** → Liste des IPs avec mismatch OS
|
||||||
|
2. **Matrice UA×OS** → User-Agent Android mais stack TCP Windows = spoof
|
||||||
|
3. **Confiance 85%** → MSS=1460 (Ethernet), scale=7, TTL≈64 → Linux réel
|
||||||
|
4. **Action** → Classer comme bot avec IP proxy
|
||||||
|
|
||||||
|
### Exemple 4 : Investiguer une IP suspecte
|
||||||
|
|
||||||
|
1. **🎯 Détections** → IP classifiée 🔴 CRITICAL
|
||||||
|
2. **Clic sur l'IP** → Synthèse : ML + TCP + JA4 + bruteforce + timeline
|
||||||
|
3. **Score de risque** : 85/100
|
||||||
|
4. **User-Agents** → 3 UA différents en 24h (rotation)
|
||||||
|
5. **TCP** → TTL initial 128 (Windows) mais UA Linux → spoof
|
||||||
|
6. **Action** → Blacklist immédiate
|
||||||
|
|
||||||
|
## 🧬 Services techniques (v2.0)
|
||||||
|
|
||||||
|
### `backend/services/tcp_fingerprint.py`
|
||||||
|
|
||||||
|
Détection multi-signal de l'OS réel basée sur la stack TCP :
|
||||||
|
|
||||||
|
```python
|
||||||
|
from backend.services.tcp_fingerprint import fingerprint_os, detect_spoof
|
||||||
|
|
||||||
|
result = fingerprint_os(ttl=52, win=5808, scale=4, mss=1452)
|
||||||
|
# → OSFingerprint(os_family="Masscan/Scanner", confidence=0.97, is_bot_tool=True)
|
||||||
|
|
||||||
|
spoof = detect_spoof(declared_ua="Chrome/Windows", fingerprint=result)
|
||||||
|
# → SpoofResult(is_spoof=True, reason="UA Windows mais stack Masscan", risk_score=30)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Poids du scoring :** TTL initial 40% + MSS 30% + fenêtre 20% + scale 10%
|
||||||
|
|
||||||
|
**Estimation hop-count :**
|
||||||
|
- TTL observé 52 → TTL initial arrondi = 64 → hops = 64 − 52 = **12**
|
||||||
|
- TTL observé 119 → TTL initial = 128 → hops = 9
|
||||||
|
|
||||||
|
**MSS → chemin réseau :**
|
||||||
|
| MSS | Réseau détecté |
|
||||||
|
|-----|---------------|
|
||||||
|
| 1460 | Ethernet standard |
|
||||||
|
| 1452 | PPPoE / DSL |
|
||||||
|
| 1420–1452 | VPN probable |
|
||||||
|
| < 1420 | Tunnel / double-encap |
|
||||||
|
|
||||||
|
### `backend/services/clustering_engine.py`
|
||||||
|
|
||||||
|
K-means++ + PCA-2D embarqués en pur Python (sans numpy/sklearn) :
|
||||||
|
|
||||||
|
```
|
||||||
|
K-means++ init : O(k·n) distances, n_init=3 runs → meilleure inertie
|
||||||
|
Power iteration : X^T(Xv) trick → O(n·d) par itération, pas de matrice n×n
|
||||||
|
Déflation Hotelling : retire PC1 de X avant de calculer PC2
|
||||||
|
```
|
||||||
|
|
||||||
|
**21 features normalisées [0,1]** — voir `FEATURES` dans le fichier.
|
||||||
|
|
||||||
|
**Nommage automatique** par priorité décroissante :
|
||||||
|
1. Pattern Masscan (mss 1440–1460, scale 3–5, TTL<60)
|
||||||
|
2. Fuzzing agressif (fuzzing_index normalisé > 0.35 ≈ valeur brute > 100)
|
||||||
|
3. UA rotatif + UA-CH mismatch simultanés
|
||||||
|
4. UA-CH mismatch seul > 80%
|
||||||
|
5. Score anomalie ML > 20% + signal comportemental
|
||||||
|
6. Classification réseau / OS par TTL/MSS
|
||||||
|
|
||||||
|
## 🗄️ Tables ClickHouse utilisées
|
||||||
|
|
||||||
|
| Table / Vue | Routes |
|
||||||
|
|---|---|
|
||||||
|
| `mabase_prod.ml_detected_anomalies` | metrics, detections, variability, analysis, clustering |
|
||||||
|
| `mabase_prod.agg_host_ip_ja4_1h` | tcp_spoofing, clustering, investigation_summary |
|
||||||
|
| `mabase_prod.view_dashboard_entities` | entities (UA, JA4, paths, query params) |
|
||||||
|
| `mabase_prod.classifications` | analysis (classifications SOC manuelles) |
|
||||||
|
| `mabase_prod.audit_logs` | audit (optionnel — silencieux si absent) |
|
||||||
|
|
||||||
|
**Conventions SQL :**
|
||||||
|
- IPs stockées en IPv6-mappé : `replaceRegexpAll(toString(src_ip), '^::ffff:', '')`
|
||||||
|
- `anomaly_score` peut être négatif : toujours utiliser `abs()`
|
||||||
|
- `fuzzing_index` peut dépasser 200 : normaliser avec `log1p`
|
||||||
|
- `multiplexing_efficiency` peut dépasser 1 : normaliser avec `log1p`
|
||||||
|
- Paramètres SQL : syntaxe `%(name)s` (dict ClickHouse)
|
||||||
|
- **SPA catch-all DOIT être le dernier router dans `main.py`**
|
||||||
|
|
||||||
## 🎨 Thème
|
## 🎨 Thème
|
||||||
|
|
||||||
Le dashboard utilise un **thème sombre** optimisé pour la sécurité :
|
Le dashboard utilise un **thème sombre** optimisé SOC (dark par défaut, clair et auto disponibles) :
|
||||||
|
|
||||||
- **Background** : Slate 900/800/700
|
- **Tokens CSS sémantiques** : `bg-background`, `bg-background-card`, `text-text-primary`, `text-text-secondary`…
|
||||||
- **Menaces** : Rouge (CRITICAL), Orange (HIGH), Jaune (MEDIUM), Vert (LOW)
|
- **Taxonomie menaces** : rouge CRITICAL / orange HIGH / jaune MEDIUM / vert LOW
|
||||||
- **Accents** : Blue (primaire), Emerald (succès)
|
- **Persistance** : `localStorage` clé `soc_theme`
|
||||||
|
- **Ne jamais utiliser** de classes Tailwind brutes (`slate-800`) — toujours les tokens sémantiques
|
||||||
|
|
||||||
## 📝 Logs
|
## 📝 Logs
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user