JA4+ Fingerprinting
Synapse implements the complete JA4+ fingerprinting suite for advanced threat detection and behavioral analysis.
Implementation Status
| Component | Status | Description |
|---|---|---|
| JA4 | ✅ Complete | TLS ClientHello fingerprinting |
| JA4T | ✅ Complete | TCP SYN options fingerprinting |
| JA4H | ✅ Complete | HTTP header fingerprinting |
| JA4L | ✅ Complete | Latency/RTT measurement |
| JA4S | ✅ Complete | TLS ServerHello fingerprinting |
| JA4X | ✅ Complete | X.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 (
gefor GET) - version: HTTP version (
11for HTTP/1.1) - cookie:
cif Cookie header present,notherwise - referer:
rif Referer header present,notherwise - count: Number of headers (excluding Cookie/Referer)
- lang: Language code (
enusfor 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:
tfor TCP,qfor QUIC - version: TLS version (
13for TLS 1.3) - ext_count: Number of extensions (02)
- alpn: ALPN protocol (
h2for 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 goneThis 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"
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
- Test JA4+ Features - Validate fingerprinting
- Configuration Reference - Detailed settings