Chromium UA but the HTTP/2 regular-header order is not chromium-shaped (JA4H)

net.h2_header_order_vs_ua  ·  convicts

networklayer
coherencecategory
7evaders caught

What it catches

JA4H regular-header order: Chromium emits the Sec-CH-UA client-hint group before user-agent on a secure origin; a Chrome/Edge UA whose header order does not is a non-browser h2 stack wearing a Chrome UA. Conservative + asymmetric (only the Chromium signature is positively classified; Firefox/Safari/non-browser stay 'unknown' and gate off) — experimental pending live-fleet validation of real-browser header orders. Extends pseudo-header coverage (net.h2_unknown_vs_ua) to the regular-header order. Ref FoxIO JA4H. v0.73.7 LIVE re-validation (detector+edge stack vs the HTTP fleet): zero positives — primp and go-tls replicate Chrome's h2 regular-header order (the edge correctly never emits h2_header_order_non_chromium), and the only non-Chromium stack (httpx/vanilla) speaks HTTP/1.1 so it has no h2 order at all. The read signal is genuinely never produced by the current fleet — chromium-impersonation stacks defeat it by replicating Chrome's order. Stays experimental/dormant: correct behavior (not a bug); needs a non-Chromium h2 stack to fire. v0.74.12 LIVE POSITIVE: the new http2-naive evader (vanilla KS_HTTP2=1 + a Chrome KS_UA — httpx over HTTP/2 with the h2 stack) emits a non-Chrome regular-header order (no Sec-CH-UA group) under a Chrome UA, so the edge emits h2_header_order_non_chromium=True and this rule fires — its FIRST live positive. Confirms the rule is correct and capable, not dead: it catches the naive h2 scraper that fakes a Chrome UA without replicating Chrome's on-wire order (where curl-impersonate/primp/go-tls defeat it by replicating). Grounded: a real Chrome (the headful captures) emits the Sec-CH-UA-first order → never fires. v0.74.13 PROMOTED experimental→active: a SECOND independent non-Chrome h2 stack corroborates — plain curl --http2 (nghttp2/C, corpus/sessions/curl-http2.json) with a Chrome UA also emits a non-Chrome order (distinct h2 SETTINGS from httpx) and trips the rule. Two independent non-Chrome stacks (httpx/Python h2, curl/nghttp2) fire it; the real-Chrome NEGATIVE is grounded across the headful Chromium capture AND all 40+ Chrome evaders/impersonators (primp/go-tls/curl-impersonate/stealth/nodriver/… all emit the Sec-CH-UA-first order → h2_header_order_non_chromium absent). Browserforge calibration has no h2 layer, so promotion cannot raise its legit-browser flag rate. RESIDUAL CAVEAT (documented, not blocking — same class as net.tcp_os_vs_ua's proxy confound): a real Chrome behind an enterprise HTTP/2 forward proxy that RE-SERIALISES the request could lose Chrome's header order and trip this; the lab edge is the first hop (sees the client directly), and such a proxy would disturb other signals too, so the residual FP is an edge case to revisit if a real-Chrome-through-h2-proxy capture becomes available. v0.74.29 FP FIX (GROUNDED on real non-automated Google Chrome 149): the 'real Chrome never fires' negative was validated only on the document NAVIGATION (where Sec-CH-UA leads). A real Chrome's fetch()/XHR requests — e.g. the collector's own POST to /ingest — interleave the Sec-CH-UA trio AROUND user-agent ('content-length,sec-ch-ua-platform,user-agent,sec-ch-ua,content-type,sec-ch-ua-mobile,accept'), so the edge fingerprinting that request emitted h2_header_order_non_chromium on a REAL Chrome (a convicting coherence FP on the most common browser). Fixed in the edge: HeaderOrderBrowser now keys on the PRESENCE of the full Sec-CH-UA trio (sec-ch-ua + sec-ch-ua-mobile + sec-ch-ua-platform), which Chrome sends on every request type and a naive non-browser stack omits — request-type-invariant. Re-validated LIVE: real Chrome 149 no longer fires (zero convicting tells now), while webkit-ua-spoof (real WebKit + Chrome UA, no trio) STILL trips it — no detection loss; the partial-trio → 'unknown' cases are unit-tested in edge h2_test.go. v0.74.35 LIVE re-validated 2026-06-20: vanilla http2-naive (KS_HTTP2=1 + a Chrome KS_UA) re-trips it on the live edge→detector stack (label bot); no drift.

Signals it reads

network.h2_header_order_non_chromium

How it fires

present

Evaders it caught 7

Bypassed by 10

Frontier evaders that reach the detector uncaught (scored only suspicious, defeating every convicting tell) — this check is not one that stops them. The red-team frontier this detection still has to convict.

← all detections