feat(bot-detector): Browser Signature Detection engine (parallel mode)

Étape A — browser_signatures.py
  Données pures : BROWSER_SIGNATURES (Chrome/Firefox/Safari), NON_BROWSER_SIGNATURES
  (curl/httpx/go), BROWSER_THRESHOLDS, DIMENSION_WEIGHTS. Valeurs H2 extraites
  des captures réelles (format Akamai avec virgules, non semicolons).

Étape B — browser_matcher.py
  Moteur vectorisé 7 dimensions (H2 SETTINGS 0.30, WINDOW_UPDATE 0.15,
  pseudo-header order 0.15, H2 PRIORITY 0.10, HTTP headers 0.15, TLS 0.10,
  JA4 dict 0.05). run_browser_matcher(df) ajoute bm_family/bm_score/bm_decision.
  CDN edge case : dimension H2 neutralisée (0.5) si has_xff=1.
  BROWSER_MATCHER_REPLACE=false par défaut (mode DUAL_MODE logging uniquement).

Étape C — 06_browser_signature_detection.sql (migration)
  Crée browser_h2_signatures (table MergeTree avec 12 fingerprints de référence).
  Recrée dict_browser_h2 depuis la table avec champ confidence (remplace CSV).

Étape D — 07_ai_features_view.sql
  +h2_wu_val dans le JOIN http_logs, +h2_window_update_value, +h2_dict_family,
  +h2_dict_confidence, +h2_window_{chrome,firefox,safari,absent},
  +h2_order_{chromesafari,firefox}, +h2_priority_present, +h2_pseudo_ord_raw,
  +tls_h2_family_mismatch (détection incohérence famille JA4 vs famille H2).

Étape E — preprocessing.py + pipeline.py
  preprocessing.py: appelle run_browser_matcher() après compute_browser_axes(),
  ajoute 7 nouvelles features binaires H2 à FEATURES et binary_features.
  pipeline.py: appelle log_dual_mode_comparison() après la classification A9.
  BROWSER_MATCHER_REPLACE=true active le remplacement du bypass.

Étape F — test_browser_matcher.py
  8 tests : Chrome/Firefox/Safari full match, curl rejeté, httpcloak partiel,
  TLS↔H2 mismatch, CDN proxy neutralisation, go net/http rejeté.
  Tous 8 PASSED (+ 36 tests existants inchangés).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
toto
2026-04-10 13:52:57 +02:00
parent c77d479d6c
commit e52cdcc01f
7 changed files with 918 additions and 5 deletions

View File

@ -0,0 +1,72 @@
-- === 06_browser_signature_detection.sql ===
-- Migration : Browser Signature Detection
--
-- 1. Création de browser_h2_signatures (table source structurée)
-- 2. Peuplement des fingerprints Akamai de référence
-- 3. Recréation de dict_browser_h2 avec le champ confidence
-- (anciennement chargé depuis CSV — remplacé par la table ClickHouse)
--
-- Appliquer avec :
-- clickhouse-client --multiquery < 06_browser_signature_detection.sql
-- --- Table source des signatures H2 ---
CREATE TABLE IF NOT EXISTS ja4_processing.browser_h2_signatures
(
h2_fingerprint String,
browser_family LowCardinality(String),
confidence Float32,
notes String
)
ENGINE = MergeTree()
ORDER BY h2_fingerprint
SETTINGS index_granularity = 128;
-- Peuplement initial depuis les données historiques (correspondance exacte CSV)
-- Format Akamai : SETTINGS|WINDOW_UPDATE|PRIORITY|PSEUDO_HEADER_ORDER
-- SETTINGS : paires clé:valeur séparées par des virgules
INSERT INTO ja4_processing.browser_h2_signatures VALUES
-- Chrome (Blink + BoringSSL, INITIAL_WINDOW_SIZE=6291456)
('1:65536,2:0,4:6291456,6:262144|15663105|0|m,a,s,p',
'Chrome', 1.0, 'Chrome 106142 stable'),
('1:65536,3:1000,4:6291456,6:262144|15663105|0|m,a,s,p',
'Chrome', 0.95, 'Chrome avec MAX_CONCURRENT_STREAMS=1000 (certains proxies)'),
('1:65536,2:0,3:100,4:6291456,6:262144|15663105|0|m,a,s,p',
'Chrome', 0.90, 'Chrome avec MAX_CONCURRENT_STREAMS=100'),
-- Firefox (Gecko + NSS, INITIAL_WINDOW_SIZE=131072, MAX_FRAME_SIZE=16384)
('1:65536,4:131072,5:16384|12517377|0|m,p,s,a',
'Firefox', 1.0, 'Firefox 90127 stable'),
('1:65536,4:131072|12517377|0|m,p,s,a',
'Firefox', 0.95, 'Firefox sans MAX_FRAME_SIZE explicite'),
('1:65536,3:100,4:131072,5:16384|12517377|0|m,p,s,a',
'Firefox', 0.90, 'Firefox avec MAX_CONCURRENT_STREAMS=100'),
-- Safari (WebKit, INITIAL_WINDOW_SIZE=65535)
('1:4096,3:100,4:65535|10485760|0|m,a,s,p',
'Safari', 1.0, 'Safari 15+ macOS et iOS'),
('1:4096,3:100,4:65535,5:16384|10485760|0|m,a,s,p',
'Safari', 0.95, 'Safari avec MAX_FRAME_SIZE=16384'),
('1:4096,3:100,4:65535,6:16384|10485760|0|m,a,s,p',
'Safari', 0.95, 'Safari avec MAX_HEADER_LIST_SIZE=16384'),
-- Edge Chromium (fingerprint H2 identique à Chrome)
('1:65536,2:0,4:6291456,6:262144|15663105|0|m,a,s,p',
'Edge', 0.9, 'Edge Chromium — fingerprint H2 identique à Chrome (dédoublonné)');
-- Recréation du dictionnaire avec le champ confidence
-- (remplace la version CSV-backed sans confidence)
DROP DICTIONARY IF EXISTS ja4_processing.dict_browser_h2;
CREATE DICTIONARY ja4_processing.dict_browser_h2
(
h2_fingerprint String,
browser_family String,
confidence Float32
)
PRIMARY KEY h2_fingerprint
SOURCE(CLICKHOUSE(query '
SELECT h2_fingerprint,
argMax(browser_family, confidence) AS browser_family,
max(confidence) AS confidence
FROM ja4_processing.browser_h2_signatures
GROUP BY h2_fingerprint
'))
LAYOUT(COMPLEX_KEY_HASHED())
LIFETIME(MIN 300 MAX 600);