Skip to main content

JA4+ Fingerprinting

Synapse implements the complete JA4+ fingerprinting suite for advanced threat detection and behavioral analysis.

Implementation Status​

ComponentStatusDescription
JA4✅ CompleteTLS ClientHello fingerprinting
JA4T✅ CompleteTCP SYN options fingerprinting
JA4H✅ CompleteHTTP header fingerprinting
JA4L✅ CompleteLatency/RTT measurement
JA4S✅ CompleteTLS ServerHello fingerprinting
JA4X✅ CompleteX.509 certificate fingerprinting

JA4 - TLS Client Fingerprint​

Format: {proto}{version}{sni}{cipher_count}{ext_count}{alpn}_{cipher_hash}_{ext_hash}

Example: t13d1516h2_8daaf6152771_b186095e22b6

What it Detects​

  • TLS version negotiation
  • Cipher suite preferences
  • TLS extensions used
  • ALPN protocols
  • SNI behavior

Use Cases​

  • Identify bot traffic
  • Detect TLS library tampering
  • Track client applications
  • Spot impersonation attempts

JA4T - TCP Fingerprint​

Format: {window_size}_{tcp_options}_{mss}_{window_scale}

Example: 65535_2-4-8-1-3_1460_7

Components​

  • Window size: TCP window size (65535)
  • TCP options: Ordered list of TCP option kinds (2=MSS, 4=SACK, 8=Timestamps, 1=NOP, 3=Window Scale)
  • MSS: Maximum segment size (1460)
  • Window scale: Window scale factor (7)

What it Detects​

  • Operating system fingerprinting
  • Network stack behavior
  • Client device type
  • NAT/proxy presence

Use Cases​

  • Identify botnet traffic
  • Detect OS spoofing
  • Track device types
  • Analyze network topology

JA4H - HTTP Header Fingerprint​

Format: {method}{version}{cookie}{referer}{count}{lang}_{headers_hash}_{cookie_names_hash}_{cookie_values_hash}

Example: ge11cr15enus_a1b2c3d4e5f6_123456789abc_def012345678

Components​

  • method: First 2 chars of HTTP method (ge for GET)
  • version: HTTP version (11 for HTTP/1.1)
  • cookie: c if Cookie header present, n otherwise
  • referer: r if Referer header present, n otherwise
  • count: Number of headers (excluding Cookie/Referer)
  • lang: Language code (enus for en-US)
  • Three 12-char SHA256 hashes

What it Detects​

  • Browser fingerprinting
  • User-agent spoofing
  • Header manipulation
  • Automation tools

Use Cases​

  • Detect scrapers
  • Identify bots
  • Track browser versions
  • Spot header tampering

JA4L - Latency Fingerprint​

Format: {rtt_microseconds}_{ttl}

Example: 12500_64 (12.5ms RTT, TTL 64)

Components​

  • Client: (ACK - SYNACK) / 2
  • Server: (SYNACK - SYN) / 2

What it Detects​

  • Network distance
  • Geographic location estimation
  • Proxy/VPN detection
  • CDN usage

Use Cases​

  • Detect proxy chains
  • Estimate geographic location
  • Identify VPN usage
  • Analyze network path

JA4S - TLS Server Response Fingerprint​

Format: {proto}{version}{ext_count}{alpn}_{cipher}_{extensions_hash}

Example: t1302h2_1301_a56c5b993250

Components​

  • proto: t for TCP, q for QUIC
  • version: TLS version (13 for TLS 1.3)
  • ext_count: Number of extensions (02)
  • alpn: ALPN protocol (h2 for HTTP/2)
  • cipher: Cipher suite (1301 = TLS_AES_128_GCM_SHA256)
  • 12-char extension hash

What it Detects​

  • Server TLS configuration
  • Backend infrastructure
  • Load balancer behavior
  • CDN fingerprinting

Use Cases​

  • Map backend infrastructure
  • Detect server changes
  • Track TLS configuration
  • Identify load balancers

JA4X - X.509 Certificate Fingerprint​

Format: {issuer_hash}_{subject_hash}_{extensions_hash}

Example: aae71e8db6d7_b186095e22b6_c1a4f9e7d8b3

Components​

Three 12-char SHA256 hashes:

  • Issuer: Issuer RDN OIDs
  • Subject: Subject RDN OIDs
  • Extensions: Certificate extension OIDs

What it Detects​

  • Certificate authority patterns
  • Certificate structure
  • Organization patterns
  • Certificate types

Use Cases​

  • Track certificate issuers
  • Detect certificate changes
  • Identify certificate types
  • Map PKI structure

Where the fingerprints are written​

Two files, with different shapes. The fingerprint stream is on by default and an agent and a proxy both write it. The collectors that poll the BPF maps need a loaded program as well as the switch, but they are not the only producer: the Linux AF_PACKET capture path and the Windows NDIS path write fingerprints with no BPF at all. The access log is a proxy file, and logging.file_logging_enabled: true is what puts it on disk; with file logging off the same record goes to the console, raw, one JSON object per line.

{log_directory}/eventbridge.log - one event per line, fingerprints at the top level:

{
"event_type": "fingerprint",
"timestamp": "2025-10-29T12:34:56.789Z",
"src_ip": "192.168.1.100",
"src_port": 54321,
"dst_ip": "10.0.0.5",
"dst_port": 8443,
"ja4": "t13d1516h2_8daaf6152771_b186095e22b6",
"ja4t": "65535_2-4-8-1-3_1460_7",
"ja4h": "ge11cr15enus_a1b2c3d4e5f6_123456789abc_def012345678"
}

{log_directory}/access.log - one request per line, fingerprints under fingerprints:

{
"event_type": "http_access_log",
"timestamp": "2025-10-29T12:34:56.789Z",
"http": { "method": "GET", "path": "/api/data" },
"network": { "src_ip": "192.168.1.100", "src_port": 54321 },
"fingerprints": {
"ja4": "t13d1516h2_8daaf6152771_b186095e22b6",
"ja4t": "65535_2-4-8-1-3_1460_7",
"ja4h": "ge11cr15enus_a1b2c3d4e5f6_123456789abc_def012345678",
"ja4s": "t1302h2_1301_a56c5b993250",
"ja4x": "aae71e8db6d7_b186095e22b6_c1a4f9e7d8b3"
}
}

Neither file has a client_ip, a tls.ja4 or an http.ja4h field. Earlier revisions of this page showed all three; a jq filter naming one prints null rather than failing. Both records omit any fingerprint that was not observed.

Configuration​

One unified log carries every fingerprint type and the BPF statistics, and it is on by default:

# /etc/synapse/config.yaml
logging:
eventbridge_log:
enabled: true # the default
# file: /var/log/synapse/eventbridge.log

fingerprint_log is an accepted alias for eventbridge_log. With file unset the path is {log_directory}/eventbridge.log — not fingerprints.log, which is the stream's old name and names no file anything writes.

tcp_fingerprint and the TCP_FINGERPRINT_* variables are gone

This page used to show a logging.tcp_fingerprint block as "kept for backward compatibility". It is not kept: the section is absent from the 0.8.8 schema, along with the other five per-type sections, and setting it has no effect beyond the Unused config options in <path> warning at startup.

The six TCP_FINGERPRINT_* environment variables this page used to list have no reader anywhere in the code either. The stream's one environment variable is LOGGING_EVENTBRIDGE_LOG_ENABLED, which takes true or false and overrides the key above.

Analyzing Fingerprints​

Extract Unique Fingerprints​

EB=/var/log/synapse/eventbridge.log

# Unique JA4 values, most frequent first
jq -r 'select(.event_type == "fingerprint" and .ja4) | .ja4' "$EB" |
sort | uniq -c | sort -rn

# Unique JA4T values
jq -r 'select(.event_type == "fingerprint" and .ja4t) | .ja4t' "$EB" |
sort | uniq -c | sort -rn

# Unique JA4H values
jq -r 'select(.event_type == "fingerprint" and .ja4h) | .ja4h' "$EB" |
sort | uniq -c | sort -rn

# Correlate source address with fingerprints
jq -r 'select(.event_type == "fingerprint")
| [.src_ip, .ja4t // "-", .ja4h // "-"] | @tsv' "$EB"
There is no fingerprint statistics endpoint

This page used to document GET /health/tcp_fingerprint_stats and the JSON body it returned. No such route exists, and none ever shipped: tcp_fingerprint_stats appears in no source file and /metrics carries no fingerprint metric, so the request answers 404.

The aggregate itself is real - synapse logs it to the console at debug level rather than serving it. See Counting Fingerprints for that command, for the counts from eventbridge.log and for the BPF maps.

Use Cases​

Bot Detection​

Combine multiple fingerprints to detect bot traffic:

# Group the clients that share a JA4
jq 'select(.event_type == "fingerprint" and .ja4 and .ja4h)
| {ip: .src_ip, ja4, ja4h}' /var/log/synapse/eventbridge.log |
jq -s 'group_by(.ja4) | map({ja4: .[0].ja4, count: length, ips: map(.ip) | unique})'

OS Fingerprinting​

Analyze TCP options to identify operating systems:

# JA4T is {window_size}_{tcp_options}_{mss}_{window_scale}
# Print window size, option order and MSS per source address
jq -r 'select(.event_type == "fingerprint" and .ja4t)
| [.src_ip, (.ja4t | split("_")[0]), (.ja4t | split("_")[1]),
(.ja4t | split("_")[2])] | @tsv' \
/var/log/synapse/eventbridge.log | sort -u

A JA4T value carries no TTL, so the TTL-based rules of thumb this page used to print (Linux: TTL=64, Windows: TTL=128) cannot be applied to it. Discriminate on the option order and the MSS: the window size alone does not separate the common stacks, which is why the old table gave Window=65535 for all three.

Threat Detection​

Identify anomalies in fingerprint patterns:

# Find rare fingerprints
jq -r 'select(.event_type == "fingerprint" and .ja4) | .ja4' \
/var/log/synapse/eventbridge.log | sort | uniq -c | sort -n | head -20

References​

License​

  • JA4 (TLS Client) - BSD 3-Clause License
  • JA4+ methods (JA4S, JA4H, JA4L, JA4T, JA4X) - FoxIO License 1.1

See JA4 License for details.

Next Steps​