Fingerprinting TLS clients with JA3

This article is a short guide to using JA3 for fingerprinting TLS clients, with possible use cases and a simple demo.

Your JA3 fingerprint is: ?

One in ? clients have this value. That’s ? bits of entropy.

How can you fingerprint TLS clients?

The principle behind JA3 fingerprinting is simple. Because TLS is a generic protocol supporting several extensions, hundreds of cipher suites and tens of elliptic curves, clients and servers must tell each other what features they support. Specifically, clients send this information in the Client Hello packet as part of the handshake in a standard, machine-readable way: this easily lends itself to creating a fingerprint of the client. JA3 does this simply concatenating the following fields:

and hashing them with MD5 to produce 32-character fingerprint that can be manipulated more easily. (The implementation is actually more complex due to bogus extensions intentionally introduced in TLS 1.3, but this is a high-level overview.)

What are some possible use cases?

JA3 fingerprints effectively depend on the software being used to connect to a TLS service. This can be used as an datapoint for HTTPS or SSH honeypots, allowing for relatively fine-grained classification of compromised devices in botnets. Indeed, this is the context where I first heard of JA3, thanks to Remco Verhoef’s work on Honeytrap.

For the same reason, HTTPS/SSH servers in highly controlled environments could reasonably implement JA3-based whitelisting of known devices as an additional layer of security: even with correct credentials, an intruder with a bad client can be identified and denied access, logged, or even transparently redirected to a honeypot, with the intruder none the wiser.

The HASSH blog post introduced the possibility of countering data exfiltration based on SSH KEXINIT, which may not be logged or detected by traditional means, with simple anomaly detection. Data exfiltration will produce a vast number of different kexinit messages, which can easily be turned into an alert.

As with all fingerprints, it poses an opportunity to track users for the advertising industry and a threat for privacy enthusiasts: JA3 fingerprints are effectively an equivalent of the User-Agent header, though less fine-grained and (at this time) impossible to change.

One more interesting scenario is fingerprint-defined routing, where an attacker who can MITM a connection can deduce vulnerabilities from the Client Hello and try to attack the connection, while transparently proxying patched clients to the correct destinations.

Finally, this talk by John Althouse (developer of JA3) goes into more detail on opportunities for client and server fingerprinting.

Possible mitigations

Fingerprinting is most useful when each client is linked to a unique fingerprint. Clients that want to avoid or deceive fingerprinting can either adopt the fingerprint of common software (thus making it impossible to trace back a fingerprint to a specific client), or adopt unique fingerprints per connection.

At the implementation level this can translate to:

This demo

The demo at the top of a page is a simple HTTPS server that builds upon Remco’s JA3 patches to the Go stdlib. You can find the source code here.

Released under CC-BY 4.0.