Wat is JA4 TLS-fingerprinting, en wat zegt het echt?
Een JA4-fingerprint beschrijft hoe een client TLS spreekt, nog voordat er ook maar één header verstuurd is. Zo is hij opgebouwd, zo verving hij JA3, dit vangt hij, en hier houdt zijn bewijskracht op.
Het eerste dat een client stuurt is geen header. Het is een TLS client hello: een lijst met ondersteunde cipher suites, de gewenste extensions, de protocolversies die hij accepteert en het applicatieprotocol dat hij hoopt te spreken. Dat komt allemaal onversleuteld binnen, vóór elke requestregel, vóór elke User-Agent. Een JA4-fingerprint is een compacte samenvatting van dat bericht, en interessant is hij om één reden: de client heeft hem niet gekozen als bewering over zichzelf. Hij rolde uit de TLS-library waarop de client gebouwd is.
Daarmee is het een ander soort signaal dan alles in de HTTP-laag. Een User-Agent is een zin die de client zelf schrijft. Een JA4-fingerprint lijkt meer op een accent.
De string lezen
Een JA4-waarde bestaat uit drie delen, gescheiden door underscores:
t13d1516h2_8daaf6152771_b186095e22b6
Het eerste deel is leesbaar zonder opzoeken. t betekent TCP, een q op die plek zou QUIC
betekenen. 13 is TLS 1.3. d zegt dat er een SNI aanwezig was, dus de client vroeg om een
domein in plaats van te verbinden met een kaal IP. 15 is het aantal aangeboden cipher suites en
16 het aantal extensions. h2 is de ALPN-waarde, deze client vroeg dus om HTTP/2.
Het tweede deel is een ingekorte hash van de cipherlijst, gesorteerd. Het derde dekt de extensions en de signature-algoritmes, ook gesorteerd. GREASE-waarden, de bewust ingestrooide onzinwaarden waarmee browsers middleboxen eerlijk houden, worden er vooraf uit gehaald.
Sorteren klinkt als een detail. Het is de hele reden dat het formaat bestaat.
Waarom JA3 het niet meer deed
JA3, de voorganger uit 2017, hashte dezelfde velden in de volgorde waarin ze over de lijn kwamen. Dat ging goed zolang TLS-stacks voorspelbaar waren. Begin 2023 introduceerde Chrome extension permutation, schudde de volgorde van zijn client hello-extensions bij elke verbinding, en één Chrome-installatie ging per request een andere JA3-hash produceren. Regels die op JA3-allowlists leunden gingen binnen één releasecyclus van precies naar nutteloos.
JA4 sorteert vóór het hashen, dus hersorteren verandert niets. Ook blijft de leesbare prefix buiten de hash, waardoor je een QUIC-verbinding van een TCP-verbinding kunt onderscheiden, of een client met maar drie cipher suites kunt opmerken, zonder database met bekende waarden. De rest van de JA4+-familie dekt andere lagen: JA4H voor HTTP-headers, JA4T voor TCP-opties, JA4S voor het antwoord van de server. Het meeste detectiewerk gebruikt de TLS-variant.
Bekijk wat de herkomst van de verbinding zegt
Wat hij vangt
De eerlijke versie: JA4 is heel goed in het vangen van clients die nooit geprobeerd hebben op te gaan in de massa.
Een Python-script met requests levert een client hello op die geen enkele browser produceert. De
standaardtransport van Go in net/http heeft zijn eigen herkenbare vorm. curl ook, een oude
OpenSSL-build ook, en de HTTP-client in de meeste SDK's eveneens. Geen daarvan verstopt zich, ze
zijn simpelweg wat ze zijn, en de fingerprint zegt dat ook als de request zich aandient als Chrome
141 op macOS.
Die mismatch is het nuttige stuk. Op zichzelf zegt een fingerprint van een Go HTTP-client niets verkeerds. Samen met een User-Agent die een browser claimt, zegt hij je dat de client zichzelf onjuist beschrijft, en de redenen daarvoor zijn meestal niet onschuldig.
Voor headless browsers geldt hetzelfde, maar zwakker. Een echte Chrome-build en een door
Playwright aangestuurde Chrome delen dezelfde TLS-stack, dus hun JA4-waarden komen overeen. Wat ze
scheidt zit elders: de webdriver-vlag, ontbrekende browseronderdelen, de timing van requests, en
of de client ooit de stylesheets en fonts ophaalt die een browser zou ophalen. Daarom is de
fingerprint één invoer en niet het oordeel, een punt dat ook langskwam in
de beste signalen voor botdetectie.
Waar hij ophoudt bewijs te zijn
Twee grenzen doen ertoe, en beide worden in verkoopteksten graag overgeslagen.
Een fingerprint is een groep, geen identiteit. Elke Chrome 141 op Windows stuurt in de praktijk dezelfde client hello. Dat zijn miljoenen mensen met één gedeelde string. Je kunt zeggen "deze verbinding kwam van iets in de vorm van Chrome 141", en over wie dat was zeg je niets. Behandel een JA4-waarde als bezoekersidentificatie en je zit er voortdurend naast, in beide richtingen.
Fingerprints zijn kopieerbaar. Met uTLS stuurt een Go-programma een client hello die byte-identiek is aan een gekozen Chrome-versie. curl-impersonate doet hetzelfde voor curl. Wie gemotiveerd genoeg is om één blogpost te lezen, laat zijn scraper een volstrekt gewone browserfingerprint tonen. Wat dat kost is moeite, en moeite is een echt filter: het verschil tussen een script dat de moeite nam en een script dat dat niet deed, is het grootste deel van de automatisering op internet. Maar een vastberaden partij komt langs dit signaal, dus alles wat alleen op JA4 steunt laat die partij door.
Er is een derde, stiller probleem. Bedrijfsproxy's met TLS-inspectie herschrijven de client hello, dus elke medewerker erachter deelt de fingerprint van de proxy in plaats van die van de browser. Blokkeer op een fingerprint en je verliest mogelijk een heel bedrijf in één keer.
Naast de andere signalen gebruiken
Wat standhoudt is stapelen, niet rangschikken. Elk signaal beantwoordt een andere vraag:
| Signaal | Vraag die het beantwoordt | Faalt wanneer |
|---|---|---|
| JA4 | Welke TLS-stack is dit? | De client uTLS gebruikt of achter een inspecterende proxy zit |
| IP-herkomst | Waarvandaan wordt verbonden? | De partij residentiële adressen huurt |
| Headless-markers | Wordt er een browser aangestuurd? | De tooling zijn eigen sporen patcht |
| Headerconsistentie | Kloppen de claims met elkaar? | De request zorgvuldig met de hand is gebouwd |
| Reverse DNS en ASN | Is deze crawler wie hij zegt? | De operator niets publiceert om te controleren |
Lees de kolom "faalt wanneer" van boven naar beneden en het punt is meteen duidelijk: de ontwijkingen verschillen per rij. Er één verslaan is makkelijk, alle vijf tegelijk is een project. Een datacenter-IP plus een Go-fingerprint plus een Chrome User-Agent is niet dubbelzinnig. Een residentieel IP met een echte Chrome-fingerprint en geen headless-sporen is waarschijnlijk een mens, en hoort ook zo behandeld te worden.
Op die logica draait Agentscan. Eén POST geeft een klasse terug, human,
known_bot, ai_agent of malicious_automation, met een confidence-waarde en de signalen
erachter, JA4 daarbij. Gecachte verdicts komen binnen 50ms terug, en dat is wat de check in een
middleware-hop laat passen in plaats van in een logreview de volgende ochtend.
Een werkbaar beleid
- Haal de fingerprint waar TLS termineert. Applicatiecode ziet de client hello nooit. Hij komt van je CDN, je load balancer of een detectie-API die ervoor staat.
- Scoor de mismatch, niet de fingerprint. Het signaal is "TLS-stack klopt niet met de geclaimde browser", niet "deze hash is fout". Een blocklist van hashes bijhouden veroudert slecht, want elke browserrelease levert nieuwe op.
- Laat hem nooit alleen beslissen. Combineer hem minimaal met herkomst. In zijn eentje vangt hij een bedrijfsproxy en mist hij een vaardige scraper, dus twee keer het verkeerde resultaat.
- Reken op verloop in de waarden. Browserupdates veranderen cipher- en extensionsets. Wat je vandaag vastzet heeft een reviewmoment nodig, anders begint het stilletjes echte gebruikers te flaggen.
- Log hem hoe dan ook. Ook als je er niets mee doet: de JA4 per request bewaren maakt het volgende incident leesbaar. Zien dat 40.000 requests één ongebruikelijke fingerprint deelden is een veel snellere start dan ruwe logs doorspitten.
Kort samengevat
JA4 vertelt je hoe een client TLS spreekt, wat lastiger terloops te vervalsen is dan welke header ook en makkelijker bewust te vervalsen dan de meeste leveranciers toegeven. Het is een sterk signaal over de vorm van de software aan de andere kant, een zwak signaal over intentie, en helemaal geen signaal over identiteit. Gebruik hem om de automatisering te vangen die nooit geprobeerd heeft menselijk te lijken, stapel hem met IP-herkomst en headless-markers voor de automatisering die dat wel deed, en bouw geen blokkeerregel die alleen op hem rust.
FAQ