-- ============================================================================= -- 13_h2_profiling.sql — Dynamic browser profiling infrastructure -- -- Vue matérialisée + table cible pour le clustering HDBSCAN hors-ligne. -- Le profile_builder.py lit cette vue quotidiennement, clusterise les sessions -- H2 similaires, et écrit les centroïdes dans auto_browser_profiles. -- -- Flux de données : -- http_logs → view_h2_profiling_raw (filtração + encodage) -- → profile_builder.py (HDBSCAN + centroïdes) -- → auto_browser_profiles (profils dynamiques) -- → browser_matcher_dynamic.py (scoring temps réel) -- ============================================================================= -- ----------------------------------------------------------------------------- -- auto_browser_profiles — centroïdes de profils navigateur auto-appris -- -- Chaque ligne = un profil issu du clustering HDBSCAN. -- Le scoring temps réel compare les sessions entrantes à ces centroïdes. -- Les profils obsolètes (> 14 jours sans observation) sont purgés par le cron. -- ----------------------------------------------------------------------------- CREATE TABLE IF NOT EXISTS ja4_processing.auto_browser_profiles ( profile_id String, detected_family LowCardinality(String), count_ips UInt64, last_seen_date Date, -- Variables continues : moyenne h2_initial_window_size_mean Int64, h2_window_update_mean UInt64, -- Tolérance sur window_update = mean + 3*std (plafonnée à la valeur mean) h2_window_update_tol UInt64, -- Variables catégorielles : mode (valeur la plus fréquente dans le cluster) pseudo_order_mode UInt8, h2_has_priority_mode UInt8, created_at DateTime DEFAULT now() ) ENGINE = ReplacingMergeTree(created_at) ORDER BY (detected_family, profile_id) TTL last_seen_date + INTERVAL 14 DAY SETTINGS index_granularity = 8192, ttl_only_drop_parts = 1; -- ----------------------------------------------------------------------------- -- view_h2_profiling_raw — extraction du vecteur H2 pour le clustering -- -- Filtre le trafic de bots évidents et encode les variables catégorielles. -- Cette vue est consommée par profile_builder.py lors du cron quotidien. -- -- Règles de filtrage : -- 1. sec_fetch_absence_rate > 0.5 ET (h2_initial_window_size = -1 OU 65535) -- → trafic curl/bots sans fingerprints H2 valides -- 2. h2_window_update = 0 (absent du preface client) -- 3. h2_pseudo_order vide (pas de HTTP/2 détecté) -- -- Encodage pseudo_order → UInt8 : -- 1 = "m,a,s,p" (Chrome/Safari) 2 = "m,p,a,s" (Firefox ancien) -- 3 = "m,s,p,a" 4 = "m,p,s,a" (curl/Firefox) -- 5 = "m,a,p,s" 0 = inconnu -- ----------------------------------------------------------------------------- CREATE OR REPLACE VIEW ja4_processing.view_h2_profiling_raw AS SELECT src_ip, -- Variable cible pour le labeling du cluster header_user_agent, -- === Variables continues (embedding) === h2_initial_window_size, h2_window_update, -- === Encodage catégoriel === multiIf( h2_pseudo_order = 'm,a,s,p', 1, h2_pseudo_order = 'm,p,a,s', 2, h2_pseudo_order = 'm,s,p,a', 3, h2_pseudo_order = 'm,p,s,a', 4, h2_pseudo_order = 'm,a,p,s', 5, 0 ) AS pseudo_order_id, h2_has_priority, -- === Agrégation utile pour le labeling === -- Taux d'absence Sec-Fetch sur la session (= count_no_sec_fetch / (hits+1)) -- Approximé ici au niveau log individuel : 1 si absent, 0 si présent toUInt8(IF(length(header_sec_fetch_site) = 0, 1, 0)) AS sec_fetch_missing, -- Taille du header table H2 (-1 = absent) h2_header_table_size, log_date FROM ja4_logs.http_logs WHERE -- HTTP/2 uniquement : pseudo_order non vide h2_pseudo_order != '' -- Exclure les sessions sans WINDOW_UPDATE (curl, outils basiques) AND h2_window_update > 0 -- Exclure les fingerprints manifestement bots : -- h2_initial_window_size = -1 (absent) ou 65535 (curl/python) AND h2_initial_window_size NOT IN (-1, 65535) -- Exclure le trafic sans UA (scanners) AND header_user_agent != '';