JA4H - HTTP Client Fingerprinting
JA4H fingerprints HTTP clients by analyzing the order and values of HTTP headers in requests. Different browsers, tools, and malware produce distinct header orderings that serve as a reliable fingerprint.
Prerequisites
- Basic understanding of HTTP headers
- Wireshark or tshark installed
What is JA4H?
JA4H creates a fingerprint from HTTP request headers. While User-Agent strings are easily spoofed, the order in which headers appear and the specific values of Accept-Language, Accept-Encoding, and other headers are much harder to fake. JA4H captures these patterns to identify HTTP clients.
JA4H Format
ge11cn07enus
a section
Method, version, header count, Accept-Language
a5dfb2c3e8f1
b section
SHA-256 of sorted header field names
b7e4a1d8f2c5
c section
SHA-256 of header values (Accept, Accept-Encoding)
c8f2e1a4d7b3
d section
SHA-256 of cookie field names
Section A Components
ge- HTTP method (ge=GET, po=POST, pu=PUT)11- HTTP version (11=HTTP/1.1, 20=HTTP/2)cn- Cookie present (c) or not (n), Referer present (r) or not (n)07- Number of headersenus- First Accept-Language value (first 4 chars)
Header Ordering Analysis
Different HTTP clients send headers in different orders. Here is how common clients differ:
GET /index.html HTTP/1.1
Host: example.com
Connection: keep-alive
sec-ch-ua: "Chromium";v="120"
sec-ch-ua-mobile: ?0
User-Agent: Mozilla/5.0 ...
Accept: text/html,application/xhtml+xml
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9GET /index.html HTTP/1.1
Host: example.com
User-Agent: curl/8.1.2
Accept: */*GET /index.html HTTP/1.1
Host: example.com
User-Agent: python-requests/2.31.0
Accept-Encoding: gzip, deflate
Accept: */*
Connection: keep-aliveUsing JA4H
Wireshark Display Filters
# Filter by JA4H fingerprint
ja4h.hash == "ge11cn07enus_a5dfb2c3e8f1_b7e4a1d8f2c5_c8f2e1a4d7b3"
# Show only POST requests
ja4h.hash matches "^po"
# Find HTTP/2 traffic
ja4h.hash matches "^..20"
# Find requests without cookies
ja4h.hash matches "^....n."tshark Extraction
# Extract JA4H from pcap
tshark -r capture.pcap -Y "http.request" \
-T fields -e ip.src -e http.host -e ja4h.hash
# Count unique JA4H by client IP
tshark -r capture.pcap -Y "http.request" \
-T fields -e ip.src -e ja4h.hash | sort | uniq -c | sort -rn
# Cross-reference with User-Agent
tshark -r capture.pcap -Y "http.request" \
-T fields -e ip.src -e http.user_agent -e ja4h.hashHands-On Exercise
Step 1: Capture HTTP Traffic
tshark -i eth0 -f "tcp port 80" -w http_capture.pcap -c 200Step 2: Generate Traffic from Different Clients
# Using curl
curl http://example.com/
# Using wget
wget -q http://example.com/ -O /dev/null
# Using Python
python3 -c "import requests; requests.get('http://example.com/')"Step 3: Compare JA4H Fingerprints
Extract and compare JA4H fingerprints from each client. Note how each produces a unique fingerprint despite all accessing the same resource. Pay attention to the header count and Accept-Language differences.