ja4

package
v0.4.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 2, 2026 License: BSD-3-Clause Imports: 6 Imported by: 0

README

ja4

Pure Go implementation of JA4+ network fingerprinting algorithms.

Part of the aoni HTTP client library.

What is JA4+?

JA4+ is a suite of methods for creating human and machine-readable fingerprints of network traffic. It improves upon JA3 by:

  • Using a locality-preserving a_b_c format for flexible partial matching
  • Sorting cipher suites and extensions to resist stunting/randomization
  • Including signature algorithms for better uniqueness
  • Supporting both TLS (JA4) and HTTP (JA4H) fingerprints

Supported Methods

Method Description Format
JA4 TLS ClientHello fingerprint t13d1516h2_<cipher_hash>_<ext_hash>
JA4H HTTP request fingerprint ge11nn03enus_<headers_hash>_<cookie_names>_<cookie_values>

Quick Start

Standalone Usage
import "github.com/lemon4ksan/aoni/ja4"

// JA4 — TLS fingerprint from ClientHello data
fingerprint := ja4.ComputeJA4(
    cipherSuites,     // []uint16 — cipher suite IDs
    extensions,       // []uint16 — extension IDs in wire order
    supportedVersions,// []uint16 — from supported_versions extension
    true,             // bool — SNI present?
    []string{"h2"},   // []string — ALPN protocols
    sigAlgorithms,    // []uint16 — signature algorithms (may be nil)
)
// "t13d1516h2_8daaf6152771_e5627efa2ab1"

// JA4H — HTTP request fingerprint
fingerprint := ja4.ComputeJA4H(
    "GET",                    // method
    "HTTP/1.1",               // protocol version
    []string{"Host", "UA"},   // header names (excl. Cookie, Referer)
    false,                    // has Cookie?
    false,                    // has Referer?
    "en-US",                  // Accept-Language
    nil, nil,                 // cookie names & values (sorted by name)
)
// "ge11nn02enus_..."
With aoni Client
import (
    "github.com/lemon4ksan/aoni"
    "github.com/lemon4ksan/aoni/ja4"
)

info := &aoni.TraceInfo{}

client := aoni.NewClient(nil).
    WithTLSFingerprint(aoni.BrowserChrome).
    WithJA4Callback(func(r ja4.JA4Report) {
        fmt.Println("JA4:", r.JA4)
    })

client.Get(ctx, "/path", aoni.TraceJA4(info))
fmt.Println(info.JA4.JA4)  // TLS fingerprint
fmt.Println(info.JA4.JA4H) // HTTP fingerprint

Fingerprint Format

JA4 (TLS)
{protocol}{version}{sni}{cipher_count}{ext_count}{alpn}_{cipher_hash}_{ext_hash}

t  13  d  15  16  h2  8daaf6152771  e5627efa2ab1
│   │   │   │   │   │       │              │
│   │   │   │   │   │       │              └─ SHA-256 of sorted extensions + sig algos
│   │   │   │   │   │       └─ SHA-256 of sorted cipher suites
│   │   │   │   │   └─ ALPN: first+last char of first protocol
│   │   │   │   └─ Extension count (GREASE excluded)
│   │   │   └─ Cipher count (GREASE excluded)
│   │   └─ SNI: d=domain, i=IP
│   └─ TLS version: 13=1.3, 12=1.2
└─ Protocol: t=TLS, q=QUIC, d=DTLS
JA4H (HTTP)
{method}{version}{cookie}{referer}{header_count}{lang}_{headers_hash}_{cookie_names}_{cookie_values}

g  e  1  1  n  n  03  enus  1c8f3b0e29d1  000000000000  000000000000
│  │  │  │  │  │   │    │         │              │              │
│  │  │  │  │  │   │    │         │              │              └─ cookie values hash
│  │  │  │  │  │   │    │         │              └─ cookie names hash
│  │  │  │  │  │   │    │         └─ sorted header names hash
│  │  │  │  │  │   │    └─ first 4 alphanum chars of Accept-Language
│  │  │  │  │  │   └─ header count (excl. Cookie, Referer)
│  │  │  │  │  └─ Referer: r=present, n=absent
│  │  │  │  └─ Cookie: c=present, n=absent
│  │  │  └─ HTTP version: 10=1.0, 11=1.1, 20=2, 30=3
│  │  └─ first 2 chars of method, lowercased

API Reference

Function Description
ComputeJA4(...) Compute TLS client fingerprint
ComputeJA4H(...) Compute HTTP client fingerprint
ParseExtensionsFromRaw(raw) Parse extension IDs from raw ClientHello bytes
IsGREASE(v) Check if a value is a TLS GREASE code point
FilterGREASE(vals) Remove GREASE values from a slice

Tests

go test ./ja4/ -v

Documentation

Overview

Package ja4 implements JA4+ network fingerprinting algorithms in pure Go.

JA4+ is a suite of methods for creating human and machine-readable fingerprints of network traffic. This package provides ComputeJA4 (TLS client fingerprinting) and ComputeJA4H (HTTP client fingerprinting), the two methods most relevant for HTTP client libraries.

Fingerprint Format

All JA4+ fingerprints use an a_b_c locality-preserving format with three sections, allowing partial matching on individual parts independently.

  • Section a: structured metadata (protocol, version, SNI, counts, ALPN)
  • Section b: hash of cipher suites (JA4) or header names (JA4H)
  • Section c: hash of extensions + signature algorithms (JA4) or cookie data (JA4H)

JA4 — TLS Client Fingerprint

The ComputeJA4 function produces a fingerprint from a TLS ClientHello:

	t13d1516h2_8daaf6152771_e5627efa2ab1

  - t: protocol (t=TLS, q=QUIC, d=DTLS)
  - 13: highest TLS version (13=TLS 1.3, 12=TLS 1.2)
  - d: SNI present (d=domain, i=IP)
  - 15: cipher suite count (GREASE excluded)
  - 16: extension count (GREASE excluded)
  - h2: first+last char of first ALPN protocol
  - 8daaf6152771: SHA-256 hash of sorted cipher suites (truncated to 12 hex chars)
  - e5627efa2ab1: SHA-256 hash of sorted extensions + sig algorithms (truncated to 12 hex chars)

JA4H — HTTP Client Fingerprint

The ComputeJA4H function produces a fingerprint from HTTP request properties:

	ge11nn03enus_1c8f3b0e29d1_000000000000_000000000000

  - ge: first 2 chars of method, lowercased
  - 11: HTTP version (10=1.0, 11=1.1, 20=2, 30=3)
  - n: no cookies (c=present)
  - n: no referer (r=present)
  - 03: header count (excluding Cookie, Referer)
  - enus: first 4 chars of Accept-Language
  - 1c8f3b0e29d1: SHA-256 hash of sorted header names (truncated to 12 hex chars)
  - 000000000000: SHA-256 hash of sorted cookie names (12 zeros if no cookies)
  - 000000000000: SHA-256 hash of cookie values in sorted-by-name order

GREASE Handling

TLS GREASE (Generate Random Extensions And Sustain Extensibility) values are filtered from all counts and hashes. Use IsGREASE to check individual values and FilterGREASE to remove them from a slice.

Integration with aoni

For automatic JA4 fingerprinting through the aoni HTTP client, use:

  • [aoni.WithTLSFingerprint] to emulate browser TLS handshakes

  • [aoni.WithJA4Callback] to receive fingerprints via a callback

  • [aoni.TraceJA4] to populate [aoni.TraceInfo] with both JA4 and JA4H

    info := &aoni.TraceInfo{} client := aoni.NewClient(nil). WithTLSFingerprint(aoni.BrowserChrome). WithJA4Callback(func(r ja4.JA4Report) { fmt.Println("TLS JA4:", r.JA4) })

    client.Get(ctx, "/path", aoni.TraceJA4(info)) fmt.Println("HTTP JA4H:", info.JA4.JA4H)

Reference Implementation

This package implements the algorithms described in the FoxIO JA4+ technical specification. The fingerprint format is designed to be resilient against cipher stunting and extension randomization used by modern browsers.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func ComputeJA4

func ComputeJA4(
	cipherSuites []uint16,
	extensions []uint16,
	supportedVersions []uint16,
	sni bool,
	alpnProtocols []string,
	sigAlgorithms []uint16,
) string

ComputeJA4 computes a JA4 TLS client fingerprint.

Parameters:

  • cipherSuites: raw cipher suite IDs from ClientHello
  • extensions: extension IDs in wire order
  • supportedVersions: from supported_versions extension
  • sni: whether SNI extension is present
  • alpnProtocols: ALPN protocol strings
  • sigAlgorithms: signature algorithm IDs in wire order (may be nil)

The fingerprint format is: {protocol}{version}{sni}{cipher_count}{ext_count}{alpn}_{cipher_hash}_{ext_hash}

func ComputeJA4H

func ComputeJA4H(
	method, proto string,
	headers []string,
	hasCookie, hasReferer bool,
	acceptLanguage string,
	cookieNames, cookieValues []string,
) string

ComputeJA4H computes an HTTP client fingerprint.

Parameters:

  • method: HTTP method (e.g. "GET", "POST")
  • proto: HTTP protocol version (e.g. "HTTP/1.1", "HTTP/2")
  • headers: header names in original order (excluding Cookie, Referer, pseudo-headers)
  • hasCookie: whether Cookie header is present
  • hasReferer: whether Referer header is present
  • acceptLanguage: Accept-Language header value
  • cookieNames: cookie names sorted by name
  • cookieValues: cookie values in sorted-by-name order

The fingerprint format is: {method}{version}{cookie}{referer}{header_count}{lang}_{headers_hash}_{cookie_names_hash}_{cookie_values_hash}

func FilterGREASE

func FilterGREASE(vals []uint16) []uint16

FilterGREASE returns a new slice with GREASE values removed.

func IsGREASE

func IsGREASE(v uint16) bool

IsGREASE reports whether v is a TLS GREASE value.

func ParseExtensionsFromRaw

func ParseExtensionsFromRaw(raw []byte) (extensions, sigAlgorithms []uint16)

ParseExtensionsFromRaw parses extension IDs from a raw TLS ClientHello message in wire order. It also extracts signature algorithms if present.

The raw format is:

2 bytes: handshake type (0x0300) + length
2 bytes: client version
32 bytes: random
1 byte + session ID: variable
2 bytes + cipher suites: variable
1 byte + compression methods: variable
2 bytes: extensions total length
then: extension entries (2-byte ID + 2-byte length + data)

Types

type Report

type Report struct {
	// JA4 is the TLS client fingerprint (e.g. "t13d1516h2_8daaf6152771_e5627efa2ab1").
	JA4 string
	// JA4H is the HTTP client fingerprint (e.g. "ge11cn04en04_9ed1ff1f7b03_cd8dafe26982").
	JA4H string
	// Protocol is the TLS protocol prefix: "t" (TLS), "q" (QUIC), "d" (DTLS).
	Protocol string
	// Version is the negotiated TLS version code: "13" (TLS 1.3), "12" (TLS 1.2), etc.
	Version string
	// SNI indicates SNI presence: "d" (domain name) or "i" (IP address).
	SNI string
	// CipherCount is the number of cipher suites (GREASE excluded).
	CipherCount int
	// ExtCount is the number of extensions (GREASE excluded).
	ExtCount int
	// ALPN is the first and last alphanumeric characters of the first ALPN protocol.
	ALPN string
}

Report holds both TLS (JA4) and HTTP (JA4H) fingerprints computed from a request.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL