@watchmecode/ja4
v1.0.0
Published
Zero-dependency JA4 + JA4H fingerprinting for Node — own TLS ClientHello parser, validated byte-for-byte against read-tls-client-hello and the FoxIO reference.
Downloads
167
Maintainers
Readme
@watchmecode/ja4
Zero-dependency JA4 + JA4H fingerprinting for Node.js.
- JA4 — TLS client fingerprint, computed from a raw ClientHello by an own,
dependency-free parser (TLS record → handshake → ciphers/extensions, GREASE
filtering). Validated byte-for-byte against
read-tls-client-helloacross real ClientHellos (SNI/ALPN variations and GREASE). - JA4H — HTTP client fingerprint, computed from the request line + headers.
Validated against the FoxIO reference (e.g. curl →
ge11nn030000_fe444ad14866_…).
JA4/JA4+ is a FoxIO standard. This library is an independent, MIT-licensed implementation with no runtime dependencies.
Install
npm install @watchmecode/ja4JA4 — TLS fingerprint
Give it the raw ClientHello bytes (a TLS record starting 0x16, or a bare
handshake message):
import { ja4FromBytes, parseClientHello, computeJa4 } from '@watchmecode/ja4';
const ja4 = ja4FromBytes(clientHelloBuffer);
// => "t13d1516h2_8daaf6152771_d8a2da3f94cd"
// or in two steps:
const hello = parseClientHello(clientHelloBuffer); // { cipherSuites, extensions, … }
const fp = computeJa4(hello);Capturing the ClientHello off a socket is out of scope here (use
read-tls-client-hello's readTlsClientHello, or your own peek); this library
does the parsing + fingerprinting.
JA4H — HTTP fingerprint
import { computeJa4h } from '@watchmecode/ja4';
const ja4h = computeJa4h({
method: 'GET',
httpVersion: '1.1', // '1.0' | '1.1' | '2.0'
headerNames: ['Host', 'User-Agent', 'Accept'], // in on-the-wire order
acceptLanguage: 'en-US,en;q=0.9',
cookie: req.headers.cookie, // raw Cookie header value, if any
});
// => "ge11nn03enus_8ddaef5d77af_000000000000_000000000000"Header names keep their on-the-wire case (matches FoxIO for HTTP/1.1); under
HTTP/2 servers normalize headers, so JA4H_b may differ there.
API
ja4FromBytes(bytes: Buffer): stringparseClientHello(bytes: Buffer): ParsedClientHellocomputeJa4(hello: ParsedClientHello): stringcomputeJa4h(input: Ja4hInput): stringisGrease(value: number): boolean
License
MIT © Anton Weber
