Random TLS Fingerprint (JA3 Fingerprint)

JANSON

This article describes a method to modify the cipher suite order within the Chromium source code. This approach enables the random generation of TLS fingerprints (such as JA3 fingerprints), allowing the browser to produce a different fingerprint with each refresh. This enhances security and facilitates deeper analysis.

1.What are TLS Fingerprint and JA3 Fingerprint?
TLS Fingerprint:
TLS fingerprinting is an advanced anti-bot identification technique. It works by analyzing the Client Hello message sent during the TLS handshake—the final stage of establishing an HTTPS connection between a client and a server—to generate a unique identifier. This "Hello" message contains parameters such as the TLS versions supported by the client, the list of cipher suites, extensions, and their specific order. Since different browsers (e.g., Chrome, Firefox) and HTTP libraries (e.g., Python Requests) have subtle yet consistent differences in their TLS protocol implementations, their Client Hello packets act like unique "digital handwriting." Anti-bot systems can hash this "handwriting" (using algorithms like JA3S) to generate a precise fingerprint, easily distinguishing, for example, a Python script masquerading as a Chrome browser from the real thing. It thus represents a significant defense barrier that is difficult to bypass.

JA3 Fingerprint:
JA3 is a widely adopted TLS fingerprinting method developed by Salesforce. It creates a "behavioral profile" for each client's network connections through a standardized process. Its core logic involves precisely extracting stable, invariant key fields from the Client Hello message during the critical TLS handshake phase—specifically, the TLS version, list of cipher suites, list of extensions, supported elliptic curves, and elliptic curve formats. JA3 then concatenates the values of these fields in a specific order and calculates a 32-bit MD5 hash value. Because different browser versions, HTTP libraries, and even malware use distinct combinations of these parameters in their TLS implementations, they generate unique JA3 fingerprints. This allows security systems to analyze this hash value like a "digital ID card," accurately identifying whether a request originates from a specific version of Chrome or a configured Python script.

2. How to obtain a TLS Fingerprint?
Website 1: https://tls.peet.ws/
Website 2: https://browserleaks.com/tls

3. How to compile a random TLS Fingerprint?
Open the source code file: third_party/boringssl/src/ssl/ssl_cipher.cc

①Add these includes at the top::

C++ Copy
#include <algorithm>
#include <random>
#include <iostream>

②Locate the following code block:

C++ Copy
 static const uint16_t kLegacyCiphers[] = {
      TLS1_CK_ECDHE_ECDSA_WITH_AES_128_CBC_SHA & 0xffff,
      TLS1_CK_ECDHE_RSA_WITH_AES_128_CBC_SHA & 0xffff,
      TLS1_CK_ECDHE_PSK_WITH_AES_128_CBC_SHA & 0xffff,
      TLS1_CK_ECDHE_ECDSA_WITH_AES_256_CBC_SHA & 0xffff,
      TLS1_CK_ECDHE_RSA_WITH_AES_256_CBC_SHA & 0xffff,
      TLS1_CK_ECDHE_PSK_WITH_AES_256_CBC_SHA & 0xffff,
      TLS1_CK_ECDHE_RSA_WITH_AES_128_CBC_SHA256 & 0xffff,
      TLS1_CK_RSA_WITH_AES_128_GCM_SHA256 & 0xffff,
      TLS1_CK_RSA_WITH_AES_256_GCM_SHA384 & 0xffff,
      TLS1_CK_RSA_WITH_AES_128_SHA & 0xffff,
      TLS1_CK_PSK_WITH_AES_128_CBC_SHA & 0xffff,
      TLS1_CK_RSA_WITH_AES_256_SHA & 0xffff,
      TLS1_CK_PSK_WITH_AES_256_CBC_SHA & 0xffff,
      SSL3_CK_RSA_DES_192_CBC3_SHA & 0xffff,
  };

It's evident that the cipher suites and their order are hardcoded and fixed within Chromium. While we cannot arbitrarily add or remove ciphers, it is feasible to randomize their order.
③Replace the source code with the following:

C++ Copy
 static uint16_t kLegacyCiphers[] = {
      TLS1_CK_ECDHE_ECDSA_WITH_AES_128_CBC_SHA & 0xffff,
      TLS1_CK_ECDHE_RSA_WITH_AES_128_CBC_SHA & 0xffff,
      TLS1_CK_ECDHE_PSK_WITH_AES_128_CBC_SHA & 0xffff,
      TLS1_CK_ECDHE_ECDSA_WITH_AES_256_CBC_SHA & 0xffff,
      TLS1_CK_ECDHE_RSA_WITH_AES_256_CBC_SHA & 0xffff,
      TLS1_CK_ECDHE_PSK_WITH_AES_256_CBC_SHA & 0xffff,
      TLS1_CK_ECDHE_RSA_WITH_AES_128_CBC_SHA256 & 0xffff,
      TLS1_CK_RSA_WITH_AES_128_GCM_SHA256 & 0xffff,
      TLS1_CK_RSA_WITH_AES_256_GCM_SHA384 & 0xffff,
      TLS1_CK_RSA_WITH_AES_128_SHA & 0xffff,
      TLS1_CK_PSK_WITH_AES_128_CBC_SHA & 0xffff,
      TLS1_CK_RSA_WITH_AES_256_SHA & 0xffff,
      TLS1_CK_PSK_WITH_AES_256_CBC_SHA & 0xffff,
      SSL3_CK_RSA_DES_192_CBC3_SHA & 0xffff,
  };

size_t arraySize = sizeof(kLegacyCiphers) / sizeof(kLegacyCiphers[0]);
std::random_device rd;
std::mt19937 rng(rd());
std::shuffle(kLegacyCiphers, kLegacyCiphers + arraySize, rng);
for (const auto& value : kLegacyCiphers) {
	std::cout << std::hex << value << std::endl;
}

After this modification, the order of the cipher suites is no longer fixed but successfully randomized.
④Compile the result::
ninja -C out/Default chrome

Update Time:Feb 03, 2026

Comments

Tips: Support some markdown syntax: **bold**, [bold](xxxxxxxxx), `code`, - list, > reference