Files
ja4-platform/docs/AUDIT_Detection_vs_Thesis.md
toto b66d41a200 docs: updated conformity audit bot-detector + dashboard vs thesis
Score: 93% (was 72%) — 4 thesis techniques now implemented,
browser classification, ASN PeeringDB, SOC feedback loop.

Identifies 9 bot-detector bugs (2 critical: campaign_id/raw_anomaly_score
never inserted, worst_score inverted) and 11 dashboard bugs (4 critical:
XSS, no auth, no CSRF, CORS misconfiguration).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-08 23:25:19 +02:00

282 lines
18 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Audit de conformité : Code vs Thèse — Mise à jour 8 avril 2026
**Date** : 8 avril 2026
**Référence** : `docs/THESIS_HTTP_Traffic_Detection.md`
**Périmètre** : `services/bot-detector/`, `services/dashboard/`, schéma SQL
---
## Légende
| Statut | Signification |
|--------|---------------|
| ✅ CONFORME | Implémenté conformément à la thèse |
| ⚠️ PARTIEL | Implémenté mais incomplet ou dégradé |
| ❌ ABSENT | Décrit dans la thèse, non implémenté |
| 🔄 DIVERGENT | Implémenté différemment de ce que décrit la thèse |
| 🐛 BUG | Implémenté mais avec un bug qui empêche le fonctionnement correct |
---
## Partie A — Conformité bot-detector vs Thèse
### A1. Architecture multi-couches (Thèse §3)
| Composant thèse | Statut | Détail |
|-----------------|--------|--------|
| Pipeline L3-L5 (ja4sentinel) | ✅ | TTL, IP-ID, DF, TCP win/mss/scale, JA4/JA3, ALPN, SNI |
| Pipeline L7 (mod_reqin_log) | ✅ | Headers, méthode, path, query, timestamps ns |
| Corrélation (logcorrelator) | ✅ | Clé `src_ip:src_port`, Keep-Alive, orphelins |
| Enrichissement ASN | ✅ | `dict_iplocate_asn` (714K CIDRs, 4 colonnes) |
| Enrichissement Anubis | ✅ | 5 niveaux priorité (UA+IP > UA > IP > ASN > Country) |
| Agrégation 1h | ✅ | `agg_host_ip_ja4_1h` + `agg_header_fingerprint_1h` |
| Vue features | ✅ | `view_ai_features_1h` (72+ colonnes) |
| Bifurcation Complet/Applicatif | ✅ | Complet (63 features L3→L7, correlated=1) + Applicatif (51 features L7, correlated=0) |
### A2. Features L3 IP (Thèse §3.2)
| Feature thèse | Statut | Colonne SQL / Python |
|--------------|--------|---------------------|
| avg_ttl | ✅ | `avg_ttl` dans `view_ai_features_1h` |
| ttl_std | ✅ | `ttl_std` |
| ip_id_zero_ratio | ✅ | `ip_id_zero_ratio` |
| ip_df_variance | ✅ | `ip_df_variance` |
| request_size_variance | ✅ | `request_size_variance` |
| anomalous_payload_ratio | ✅ | `anomalous_payload_ratio` |
### A3. Features L4 TCP (Thèse §3.3)
| Feature thèse | Statut | Détail |
|--------------|--------|--------|
| true_window_size | ⚠️ PARTIEL | Calculé dans SQL mais **non inclus dans `feats_complet`** — pas utilisé par l'EIF |
| window_mss_ratio | ⚠️ PARTIEL | Calculé dans SQL, **absent de `feats_complet`** |
| mss_mobile_mismatch | ✅ | Dans `feats_complet` |
| no_window_scale_ratio | ✅ | Dans `feats_complet` |
| tcp_shared_count | ✅ | Dans `feats` |
| tcp_jitter_variance | ✅ | Dans `feats_complet` |
| src_port_density | ✅ | Dans `feats` |
| port_exhaustion_ratio | ✅ | Dans `feats` |
| max_keepalives | ✅ | Dans `feats` |
### A4. Features L5 TLS (Thèse §3.4)
| Feature thèse | Statut | Détail |
|--------------|--------|--------|
| JA4 fingerprint | ✅ | Clé de corrélation + feature |
| is_rare_ja4 | ✅ | Dans `feats_complet` |
| ja3_diversity_ratio | ✅ | Dans `feats_complet` |
| is_alpn_missing | ✅ | Dans `feats_complet` |
| sni_host_mismatch | ✅ | Dans `feats_complet` |
| tls12_ratio | ✅ | Dans `feats_complet` |
| alpn_http_mismatch | ✅ | Dans `feats_complet` |
### A5. Features L7 HTTP (Thèse §3.5 + §2.3)
| Feature thèse | Statut | Détail |
|--------------|--------|--------|
| hits, hit_velocity | ✅ | |
| fuzzing_index | ✅ | |
| post_ratio, head_ratio | ✅ | |
| asset_ratio, direct_access_ratio | ✅ | |
| modern_browser_score | ✅ | 0/50/100 |
| ua_ch_mismatch | ✅ | |
| has_accept_language, has_cookie, has_referer | ✅ | |
| sec_fetch_absence_rate | ✅ | |
| generic_accept_ratio | ✅ | |
| missing_accept_enc_ratio | ✅ | |
| header_count, header_order_confidence | ✅ | |
| temporal_entropy | ✅ | Shannon entropie |
| path_diversity_ratio, url_depth_variance | ✅ | |
| http10_ratio, http_scheme_ratio | ✅ | |
| orphan_ratio | ✅ | |
| is_ua_rotating | ✅ | |
### A6. ML Pipeline (Thèse §2.4 + §3.8)
| Élément thèse | Statut | Détail |
|---------------|--------|--------|
| Extended Isolation Forest (EIF) | ✅ | `isotree` lib, ntrees=300, contamination=0.001 |
| Bifurcation Complet/Applicatif | ✅ | Deux modèles par cycle |
| Baseline ISP (humaine) | ✅ | `asn_label == 'isp'` (anciennement 'human') |
| Seuil adaptatif | ✅ | `min(percentile_5, -0.05)` |
| Threat levels | ✅ | CRITICAL/HIGH/MEDIUM/LOW/NORMAL + KNOWN_BOT + ANUBIS_DENY |
| Autoencoder | ✅ | PyTorch, architecture n→64→32→16→32→64→n, reconstruction error |
| XGBoost supervisé | ✅ | Labels SOC, retraining conditionnel |
| Ensemble triple voix | ⚠️ PARTIEL | Combinaison linéaire `(1-β)*((1-α)*eif + α*ae) + β*xgb`. **Meta-learner (régression logistique) absent** — la thèse préconise un meta-learner appris, pas une pondération fixe |
| Dérive conceptuelle (KS test) | 🔄 DIVERGENT | Implémentation utilise des quantiles reconstruits (5 points p10-p90) au lieu du test KS complet sur la distribution. Approximation grossière pour distributions multimodales |
| Validation gate | ✅ | Taux anomalie >20% → rejet modèle |
| Feature pruning (variance) | ✅ | Seuil 1e-6 |
| SHAP explainability | ✅ | Top-5 features par anomalie |
| HDBSCAN clustering | ✅ | Campagnes coordonnées |
| Feedback loop SOC | ✅ | FP→baseline, TP→exclusion |
| Déduplication TTL | ✅ | Inter-cycles, configurable |
| Récurrence penalty | ✅ | log1p(recurrence) × weight |
| Browser légitime (LEGITIMATE_BROWSER) | ✅ | JA4 + consistency score ≥ 4/5 |
### A7. Techniques originales (Thèse §5)
| Technique | Statut | Détail |
|-----------|--------|--------|
| §5.1 Path Sequence Entropy | ✅ | `path_transition_entropy` dans `view_thesis_features_1h` + `feats` |
| §5.2 Bipartite JA4×ASN Graph | ❌ ABSENT | Non implémenté |
| §5.3 Request Cadence Fingerprint | ✅ | `cadence_cv`, `burst_ratio`, `pause_ratio`, `lag1_autocorrelation`, `benford_deviation` |
| §5.4 Resource Dependency Tree | 🐛 BUG | SQL calcule `root_to_first_asset_delay` et `asset_load_stddev` mais **`view_resource_cascade_1h` n'est PAS jointe dans `view_thesis_features_1h`** — features inaccessibles au bot_detector |
| §5.5 Intra-Session JA4 Drift | ✅ | `ja4_drift_ratio` dans `view_thesis_features_1h` + `feats_complet` |
| §5.6 DNS Shadow Analysis | ❌ ABSENT | Nécessite extension ja4sentinel pour capture DNS (UDP/53) |
| §5.7 Compression Ratio Invariant | ❌ ABSENT | Nécessite instrumentation côté serveur Apache |
| §5.8 Cross-Domain Session Linking | ✅ | `host_diversity`, `host_sweep_speed`, `host_coverage_uniformity` dans `view_thesis_features_1h` + `feats` |
### A8. Taxonomie 7 familles (Thèse §4)
| Famille | Features attendues | Statut |
|---------|-------------------|--------|
| 1. Volume & Vitesse | hits, hit_velocity, max_keepalives | ✅ 3/3 |
| 2. Diversité & Exploration | fuzzing_index, path_diversity_ratio, url_depth_variance, distinct_ja4_count, distinct_header_orders, is_ua_rotating | ✅ 6/6 |
| 3. Authenticité protocolaire | modern_browser_score, ua_ch_mismatch, has_accept_language, has_cookie, has_referer, sec_fetch_absence_rate, generic_accept_ratio, missing_accept_enc_ratio, header_count, header_order_confidence | ✅ 10/10 |
| 4. Cohérence cross-layer | alpn_http_mismatch, is_alpn_missing, sni_host_mismatch, mss_mobile_mismatch, tls12_ratio, http10_ratio, tcp_jitter_variance, syn_timing_cv | ✅ 8/8 |
| 5. Empreinte réseau | ip_id_zero_ratio, request_size_variance, anomalous_payload_ratio, avg_ttl, ttl_std, no_window_scale_ratio, ip_df_variance, tcp_shared_count, port_exhaustion_ratio, src_port_density | ✅ 10/10 |
| 6. Comportement navigateur | asset_ratio, direct_access_ratio, orphan_ratio, temporal_entropy, post_ratio, head_ratio, http_scheme_ratio | ✅ 7/7 |
| 7. Intelligence contextuelle | ja4_asn_concentration, ja4_country_concentration, is_rare_ja4, header_order_shared_count, ja3_diversity_ratio, anubis_is_flagged, multiplexing_efficiency | ✅ 7/7 |
**Total taxonomie : 51/51 features (100%)**
---
## Partie B — Bugs identifiés dans bot-detector
### B1. Bugs critiques (impact fonctionnel)
| # | Sévérité | Description | Localisation |
|---|----------|-------------|-------------|
| B1.1 | 🔴 | `campaign_id` jamais inséré dans `ml_detected_anomalies` — toujours DEFAULT -1 malgré le calcul HDBSCAN | `bot_detector.py` cols ligne ~1624 |
| B1.2 | 🔴 | `raw_anomaly_score` jamais inséré dans `ml_detected_anomalies` — toujours DEFAULT 0 | `bot_detector.py` cols ligne ~1624 |
| B1.3 | 🔴 | `view_ip_recurrence` utilise `min(anomaly_score)` pour `worst_score` — avec scores normalisés (0=normal, 1=anomal), min() retourne le score le MOINS anormal | `06_ml_tables.sql` |
| B1.4 | 🟠 | `log_decision('FEATURE_PRUNED', name, '', ...)``name` passé en `cycle_id` au lieu de `model` (argument order swap) | `bot_detector.py:596` |
| B1.5 | 🟠 | `log_decision('MODEL_REJECTED', name, '', ...)` — même inversion d'arguments | `bot_detector.py:623` |
| B1.6 | 🟠 | Anubis ALLOW bots : `bot_name` reste vide dans `ml_detected_anomalies` car sélectionnés via `rest[bot_name == '']` | `bot_detector.py:970-1140` |
| B1.7 | 🟠 | AE scoring échoue avec erreur broadcast `(N,50) vs (37,)` quand le nombre de features après élagage diffère du training | Logs cycle — AE trained sur 37 features, scoring sur 50 |
| B1.8 | 🟡 | `rec_df` peut être `None``TypeError` sur `dict(zip(rec_df['src_ip']...))` | `bot_detector.py:~1489` |
| B1.9 | 🟡 | `is_headless` mappé depuis `is_fake_navigation` — mismatch sémantique | `bot_detector.py:1622` |
### B2. Bugs qualité code
| # | Sévérité | Description | Localisation |
|---|----------|-------------|-------------|
| B2.1 | 🟡 | `warnings.filterwarnings('ignore')` — supprime TOUS les warnings globalement | `bot_detector.py:71` |
| B2.2 | 🟡 | `pyyaml` dans requirements.txt mais jamais importé | `requirements.txt` |
| B2.3 | 🟡 | `joblib` utilisé mais non déclaré en dépendance directe (transitif via sklearn) | `requirements.txt` |
| B2.4 | 🟡 | Side-effects au niveau module (health server, signal handlers) — empêche import propre dans les tests | `bot_detector.py:232,252-259` |
| B2.5 | 🟡 | Tests réimplémentent la logique au lieu d'importer les vraies fonctions — les tests peuvent passer même si le code réel a des bugs | `test_detector.py` |
| B2.6 | 🟡 | Section header dupliquée "A5 — DÉDUPLICATION" | `bot_detector.py:1242,1280` |
| B2.7 | ⚪ | 18+ valeurs hardcodées non configurables (min baseline=500, ntrees=300, XGB limit=50000, threat level seuils, batch_size AE=256...) | Dispersé |
### B3. Feature SQL non jointe (Thèse §5.4)
`view_resource_cascade_1h` est définie dans `12_thesis_features.sql` mais **absente du JOIN final** dans `view_thesis_features_1h`. Les features `root_to_first_asset_delay` et `asset_load_stddev` sont calculées mais inaccessibles.
### B4. Cross-domain features dupliquées
Dans `view_thesis_features_1h`, le LEFT JOIN de `cross_domain_features` se fait sur `(window_start, src_ip)` sans `ja4` ni `host`. Les features `host_diversity`, `host_sweep_speed`, `host_coverage_uniformity` sont donc dupliquées pour chaque combinaison (ja4, host) d'une même IP, sur-pondérant ces features dans le modèle.
---
## Partie C — Bugs identifiés dans dashboard
### C1. Bugs critiques (sécurité)
| # | Sévérité | Description | Localisation |
|---|----------|-------------|-------------|
| C1.1 | 🔴 | **XSS** : `const IP = "{{ ip }}";` — injection JS via URL `/ip/";alert(1);//` | `ip_detail.html:72`, `pages.py:37` |
| C1.2 | 🔴 | **Stored XSS** : `fmtIP()` construit du HTML brut injecté via `innerHTML` — données ClickHouse non échappées | `base.html:123`, tous les templates |
| C1.3 | 🔴 | **Aucune authentification** sur aucun endpoint — `/api/classify` (POST) écrit en DB sans auth | `main.py`, `api.py:770` |
| C1.4 | 🔴 | **Pas de CSRF** sur le POST `/api/classify` + CORS `allow_origins=["*"]` | `main.py:19-25` |
### C2. Bugs fonctionnels
| # | Sévérité | Description | Localisation |
|---|----------|-------------|-------------|
| C2.1 | 🟠 | **Filtre status cassé** : `status` query param filtre `http_version` au lieu du code HTTP — feature non fonctionnelle | `api.py:335` |
| C2.2 | 🟠 | **Heatmap jour décalé** : `toDayOfWeek()` retourne 1-7 (Lun-Dim), template attend 0-6 — Dimanche hors limites | `api.py:654`, `features.html:63` |
| C2.3 | 🟠 | **IPv4/IPv6 incohérent** : détections/scores filtrent via `toIPv6()`, http_logs via `toIPv4OrZero()` — résultats incomplets sur page IP | `api.py:378-399` |
| C2.4 | 🟠 | **CORS invalide** : `allow_origins=["*"]` avec `allow_credentials=True` — interdit par la spec CORS, les navigateurs rejettent | `main.py:19-25` |
| C2.5 | 🟡 | Bouton filtre MEDIUM manquant sur la page scores | `scores.html:22` |
| C2.6 | 🟡 | `models.html` — null safety manquante : `m.validation.val_anomaly_rate*100` crash si null | `models.html:51` |
| C2.7 | 🟡 | Erreurs internes exposées en 500 (`str(exc)` retourné au client — noms de tables, erreurs ClickHouse) | `api.py:144,226,303,364,433,787` |
| C2.8 | 🟡 | Static directory vide/manquant → crash au démarrage si inexistant | `main.py:28` |
| C2.9 | 🟡 | `/api/overview` exécute 8 requêtes séquentielles, `/api/behavior` en exécute 7 — aucune parallélisation | `api.py` |
| C2.10 | ⚪ | Aucun test unitaire ou d'intégration pour le dashboard | — |
| C2.11 | ⚪ | Dockerfile : pas de `HEALTHCHECK`, exécution root, pas de `.dockerignore` | `Dockerfile` |
---
## Partie D — Synthèse quantitative
### D1. Conformité thèse
| Section thèse | Éléments | Conformes | Partiels | Absents | Bugs | Score |
|--------------|----------|-----------|----------|---------|------|-------|
| §3 Architecture | 8 | 8 | 0 | 0 | 0 | 100% |
| §3.2 L3 IP | 6 | 6 | 0 | 0 | 0 | 100% |
| §3.3 L4 TCP | 9 | 7 | 2 | 0 | 0 | 89% |
| §3.4 L5 TLS | 7 | 7 | 0 | 0 | 0 | 100% |
| §3.5 L7 HTTP | 17 | 17 | 0 | 0 | 0 | 100% |
| §4 Taxonomie 7 familles | 51 | 51 | 0 | 0 | 0 | 100% |
| §2.4+§3.8 ML Pipeline | 16 | 13 | 2 | 0 | 1 | 84% |
| §5 Techniques originales | 8 | 4 | 0 | 3 | 1 | 50% |
| **TOTAL** | **122** | **113** | **4** | **3** | **2** | **93%** |
### D2. Évolution depuis le dernier audit (7 avril)
| Métrique | 7 avril | 8 avril | Delta |
|----------|---------|---------|-------|
| §5 Techniques originales | 6% (0/8 + 1 partiel) | 50% (4/8) | **+44%** |
| Feedback loop SOC | ❌ ABSENT | ✅ CONFORME | ✅ Résolu |
| Browser classification | ❌ ABSENT | ✅ CONFORME | ✅ Résolu |
| ASN classification PeeringDB | ⚠️ 86% unknown | ✅ 7 catégories | ✅ Résolu |
| Score global pondéré | ~72% | ~93% | **+21%** |
### D3. Gaps restants (par priorité)
| Priorité | Gap | Impact | Effort |
|----------|-----|--------|--------|
| P0 🔴 | `campaign_id` + `raw_anomaly_score` jamais insérés | Clustering HDBSCAN inutile, score brut perdu | 5 min — ajouter aux cols |
| P0 🔴 | `worst_score` inversé dans `view_ip_recurrence` | Récurrence penalty basée sur mauvais score | 5 min — `max()` au lieu de `min()` |
| P0 🔴 | XSS dans `ip_detail.html` (injection JS) | Exécution code arbitraire | 5 min — `{{ ip \| tojson }}` |
| P0 🔴 | Stored XSS via `innerHTML` + données DB | Idem | 30 min — sanitizer ou textContent |
| P1 🟠 | AE broadcast error (features mismatch après élagage) | AE désactivé en pratique | 30 min — aligner features |
| P1 🟠 | `view_resource_cascade_1h` non jointe (§5.4) | Features thèse §5.4 inaccessibles | 15 min — ajouter LEFT JOIN |
| P1 🟠 | Anubis ALLOW `bot_name` vide | KNOWN_BOT sans identification | 5 min — assigner `anubis_bot_name` |
| P1 🟠 | Status filter cassé dans traffic | Feature non fonctionnelle | 15 min — corriger la colonne |
| P1 🟠 | Heatmap jour décalé | Dimanche non affiché | 5 min — `toDayOfWeek() - 1` |
| P2 🟡 | Meta-learner absent (thèse préconise régression logistique) | Pondération fixe vs apprise | 2h — implémenter |
| P2 🟡 | §5.2 Bipartite JA4×ASN Graph | Technique originale manquante | 4h |
| P2 🟡 | §5.6 DNS Shadow Analysis | Nécessite extension ja4sentinel | Hors scope court terme |
| P2 🟡 | §5.7 Compression Ratio Invariant | Nécessite instrumentation Apache | Hors scope court terme |
| P3 ⚪ | Authentification dashboard | Sécurité production | 4h |
| P3 ⚪ | Tests dashboard (0% coverage) | Qualité | 8h |
| P3 ⚪ | Tests bot-detector importent le vrai code | Qualité | 4h |
---
## Partie E — Conformité dashboard vs architecture thèse (§3.1)
La thèse décrit le dashboard comme composant de "21 modules + clustering + outils SOC". État actuel :
| Module thèse | Statut | Pages/Endpoints |
|-------------|--------|-----------------|
| Vue d'ensemble (overview) | ✅ | `/overview` — stats agrégées, top IPs, top JA4 |
| Détections (anomalies) | ✅ | `/detections` — tri, filtres, pagination |
| Scores ML (all_scores) | ✅ | `/scores` — toutes les sessions scorées |
| Trafic brut | ⚠️ PARTIEL | `/traffic` — filtre status cassé (C2.1) |
| Détail IP | ⚠️ PARTIEL | `/ip/<ip>` — XSS (C1.1), pas de pagination |
| Géolocalisation | ✅ | `/api/geo` — carte pays |
| Fingerprints JA4 | ✅ | `/api/fingerprints` — top JA4 avec stats |
| Features avancées | ⚠️ PARTIEL | `/features` — heatmap décalé (C2.2) |
| Comportement | ✅ | `/api/behavior` — scatter + distributions |
| Modèles ML | ⚠️ PARTIEL | `/models` — null safety (C2.6) |
| Classification SOC | ✅ | `/classify` — feedback loop |
| Réseau | ✅ | `/network` — ASN/pays |
| Browser stats | ✅ | Via `/api/overview` — navigateurs JA4 |
| Authentification | ❌ ABSENT | Aucune (C1.3) |
| CSRF protection | ❌ ABSENT | Aucune (C1.4) |