Skip to content

fooinha/nginx-ssl-ja3

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

82 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

nginx-ssl-ja3

nginx module for SSL/TLS JA3 fingerprinting.

Description

This module adds nginx variables that expose the JA3 TLS client fingerprint and its MD5 hash. JA3 fingerprints the TLS ClientHello by combining the TLS version, cipher suites, extensions, elliptic curves, and elliptic curve point formats into a string that is then MD5-hashed.

Note: Chrome 110+ and recent Firefox builds randomise the order of TLS ClientHello extensions, which makes standard JA3 unstable across requests from the same browser. Compile with --with-cc-opt='-DJA3_SORT_EXT' to sort extensions before fingerprinting (produces a non-standard but stable fingerprint).


Variables

HTTP

Variable Description
$http_ssl_ja3 JA3 fingerprint string for an HTTP/HTTPS connection
$http_ssl_ja3_hash MD5 hash of $http_ssl_ja3
http {
    server {
        listen              127.0.0.1:443 ssl;
        ssl_certificate     cert.pem;
        ssl_certificate_key rsa.key;
        return 200          "$http_ssl_ja3\n$http_ssl_ja3_hash\n";
    }
}

Example fingerprint string:

771,4865-4866-4867-49195-49199-49196-49200-52393-52392-49171-49172-156-157-47-53-10,0-23-65281-10-11-35-16-5-13-18-51-45-43-21,0-29-23-24,0

Stream

Variable Description
$stream_ssl_ja3 JA3 fingerprint string for a TCP stream SSL connection
$stream_ssl_ja3_hash MD5 hash of $stream_ssl_ja3
stream {
    server {
        listen              127.0.0.1:12345 ssl;
        ssl_certificate     cert.pem;
        ssl_certificate_key rsa.key;
        return              "$stream_ssl_ja3\n$stream_ssl_ja3_hash\n";
    }
}

Build

Requirements

  • nginx ≥ 1.11.2 (stream support) with --with-http_ssl_module --with-stream_ssl_module
  • OpenSSL ≥ 1.1.1 (for SSL_CTX_set_client_hello_cb) — OpenSSL 3.x recommended

The module uses the nginx ClientHello early callback to capture cipher suites and extensions before the handshake completes, which requires a small patch to nginx source. No patch to OpenSSL itself is needed when using a system OpenSSL ≥ 1.1.1.

Nginx patches

Patch file nginx version
patches/nginx.1.27.2.ssl.extensions.patch nginx 1.27.2
patches/nginx.1.29.8.ssl.extensions.patch nginx 1.29.x / 1.30.x / 1.31.x
patches/nginx.1.23.1.ssl.extensions.patch nginx 1.23.1

Build steps

# Clone nginx (pinned to the version matching your patch)
git clone --depth 1 --branch release-1.27.2 https://github.com/nginx/nginx.git
cd nginx

# Apply the nginx patch
patch -p1 < /path/to/nginx-ssl-ja3/patches/nginx.1.27.2.ssl.extensions.patch

# Configure with the module
./configure \
    --add-module=/path/to/nginx-ssl-ja3 \
    --with-http_ssl_module \
    --with-stream_ssl_module \
    --with-stream \
    --with-debug

# Build and install
make && make install

When using a custom OpenSSL build via --with-openssl=/path, nginx compiles OpenSSL as part of its own build. The module's compile-time feature detection skips the link test automatically in that case.


Tests

Docker (recommended — runs everything)

docker compose -f docker/docker-compose.yml run --rm nginx-test

This builds a lean Debian bookworm image with system OpenSSL 3, patches and builds nginx 1.27.2, runs 71 C unit tests with lcov coverage, and runs 25 Perl integration tests (HTTP + stream).

C unit tests only

make -C t/unit all       # build and run
make -C t/unit coverage  # run + print lcov coverage table

Perl integration tests

# nginx must already be installed and TEST_NGINX_BINARY set
export TEST_NGINX_BINARY=/usr/local/nginx/sbin/nginx
prove -v t/http_ssl_ja3.t t/stream_ssl_ja3.t

Docker

A dev image (with a from-source OpenSSL build) and a lean test image are available:

# Full dev environment
docker compose -f docker/docker-compose.yml up --build nginx-dev

# Lean test runner (CI-style)
docker compose -f docker/docker-compose.yml run --rm nginx-test

Contributors

  • @fooinha — author and maintainer
  • @Sessa93 — OpenSSL 1.1.1-pre9 support
  • @bartebor — fix SSL session leak
  • @catap — C99 mode fix; skip OpenSSL feature test for custom builds
  • @tiandrey — nginx 1.14.0 and OpenSSL patch updates
  • @k1k — allow building without stream module
  • @gbilic — JA3_SORT_EXT extension sorting feature
  • @oowl — static-link build fix (NGX_LIBDL/NGX_LIBPTHREAD)
  • @kamyabzad — fix segfault in ClientHello callback
  • @climagabriel — replace deprecated SSL_get0_raw_cipherlist with SSL_client_hello_get0_ciphers
  • @vobloeb — nginx 1.29.x / 1.30.x / 1.31.x patch
  • @rc5hack — compiler flag and git clone improvements

Fair Warning

THIS IS NOT PRODUCTION READY.

No guarantee of stability. It will most probably blow up in real-life scenarios.

About

nginx module for SSL/TLS ja3 fingerprint.

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors