O que é fingerprint TLS e como funciona o JA3?

Publicado:

21 min de leitura

Acar Diveroli
Autor: Acar Diveroli
Dos campos do ClientHello no portão leitor sobe o resumo JA3 até o painel; o cliente de script não consegue passar

Você tem um script escrito em Python. Ele sai por um proxy, o endereço IP muda a cada requisição e no cabeçalho User-Agent você colocou uma linha atual do Chrome. Mesmo assim o site responde 403 logo na primeira requisição. Abra o mesmo endereço pelo mesmo IP em um navegador de verdade e a página carrega. A diferença está em um pacote que viaja antes de qualquer cabeçalho HTTP: a mensagem ClientHello, que abre a conexão criptografada. Olhando como essa mensagem é montada, um site percebe se está falando com o Chrome, com o Python ou com o curl sem ler um único cabeçalho.

Neste texto vemos o handshake TLS, os campos dentro do ClientHello, como a string JA3 é calculada a partir desses campos e o que o JA4 faz de diferente. Depois passamos à pergunta que mais chega até nós: um proxy muda o fingerprint TLS? No final há um exemplo em Python já testado que mostra localmente a string JA3 do seu próprio cliente.

O que é o handshake TLS?

TLS é o protocolo que criptografa o tráfego entre o navegador e o site; o https:// na barra de endereços mostra que você está usando ele. Antes de a criptografia começar, os dois lados precisam combinar qual versão, qual suíte de cifra e qual chave usar. Essa negociação curta se chama handshake. A versão atual, o TLS 1.3, está definida na RFC 8446, e o handshake corre mais ou menos assim:

  1. O cliente se conecta ao servidor por TCP e envia uma mensagem ClientHello. Nela vão as suítes de cifra e as extensões que ele suporta, além da sua parcela de chave.
  2. O servidor escolhe da lista e responde com ServerHello. Daí em diante os dois lados conseguem derivar a chave compartilhada.
  3. O servidor envia o certificado e uma mensagem Finished que prova a integridade do handshake, ambos criptografados.
  4. O cliente verifica o certificado e manda a sua própria mensagem Finished.
  5. Só então a primeira requisição HTTP (GET /, cabeçalhos, User-Agent) viaja, dentro do canal criptografado.

Para o fingerprint, o que importa é o passo um. O ClientHello é enviado antes de existir qualquer chave compartilhada, então vai sem criptografia; o servidor, o CDN à frente dele e cada equipamento de rede no caminho conseguem lê-lo como está, antes de qualquer cabeçalho, cookie ou linha de JavaScript.

Quais campos a mensagem ClientHello contém?

O ClientHello é uma lista em que o cliente diz «isto é o que eu sei falar». Estes são os campos usados no fingerprint:

CampoO que carrega?Por que diferencia?
legacy_versionO campo de versão antigo. Por compatibilidade, clientes de TLS 1.3 ainda escrevem aqui o valor do TLS 1.2 (771)Sozinho diz pouco; a versão real está em uma extensão
cipher_suitesSuítes de cifra suportadas, em ordem de preferênciaTanto a lista quanto a ordem variam de biblioteca para biblioteca
extensionsOs números de tipo das extensões: server_name (0), supported_groups (10), signature_algorithms (13), ALPN (16), supported_versions (43), key_share (51) e outrasQuais extensões existem, e em que ordem, é próprio de cada software
supported_groupsCurvas e grupos utilizáveis na troca de chaves (29 = x25519, 23 = secp256r1)Grupos novos chegam primeiro aos navegadores
ec_point_formatsFormatos de ponto de curva elípticaAlgumas bibliotecas enviam três valores, outras só um
signature_algorithmsAlgoritmos de assinatura aceitos, em ordemO JA3 não usa, o JA4 usa
ALPNO protocolo de aplicação que o cliente quer falar (h2, http/1.1)Um navegador pede h2; clientes simples quase nunca pedem

Quem preenche essas listas não é a sua aplicação, e sim a biblioteca TLS por baixo dela. O Chrome usa BoringSSL e o Firefox usa NSS. O módulo ssl do Python e o Node.js são construídos sobre OpenSSL. O curl que vem com o Windows usa Schannel, a pilha TLS do próprio sistema operacional. Cada biblioteca tem uma lista de cifras padrão, um conjunto de extensões e uma ordenação diferentes; é assim que a identidade do cliente vaza para o primeiro pacote do handshake.

O que é um fingerprint TLS?

Um fingerprint TLS são essas listas do ClientHello reduzidas a uma string curta e comparável. Três propriedades o separam de outros sinais:

  • Ele é passivo. O site não faz o cliente executar nada; apenas lê o primeiro pacote que chega. Desligar o JavaScript ou apagar os cookies não muda o resultado.
  • Ele independe dos cabeçalhos. O User-Agent é uma linha de texto e muda com uma linha de código em qualquer biblioteca HTTP. O ClientHello vem do comportamento compilado da biblioteca.
  • Ele identifica o software, não a pessoa. Todo mundo que roda a mesma versão do Chrome no mesmo sistema operacional produz o mesmo valor. A camada de JavaScript (canvas, fontes, WebGL), que tenta separar dispositivos um a um, está em O que é fingerprint do navegador?.

O método não foi inventado para detectar bots. O primeiro uso foi segurança de rede: malware criptografa o tráfego, mas a biblioteca TLS e as configurações costumam ser fixas. Um time de segurança que não enxerga o conteúdo ainda reconhece a mesma família de software pelo desenho do ClientHello.

O que é o JA3 e como ele é calculado?

O JA3 é o primeiro método difundido que amarrou essa ideia a um formato padrão. Foi desenvolvido na Salesforce em 2017 por John Althouse, Jeff Atkinson e Josh Atkins e publicado como código aberto. Como descreve o repositório JA3 da Salesforce, o cálculo tem cinco passos:

  1. Da mensagem ClientHello são tirados cinco campos: versão do TLS, suítes de cifra, extensões, curvas (supported_groups) e formatos de ponto de curva elíptica.
  2. Os valores de cada campo são convertidos para números decimais e juntados com - na ordem em que aparecem na mensagem.
  3. Os cinco campos são unidos com ,. Se um campo estiver vazio, o lugar dele fica vazio.
  4. Valores GREASE (explicamos abaixo) não entram nas listas.
  5. Tira-se o hash MD5 da string resultante. Esse hash de 32 caracteres é o fingerprint JA3.

O exemplo do próprio repositório é este:

text
769,47-53-5-10-49161-49162-49171-49172-50-56-19-4,0-10-11,23-24-25,0
→ ada70206e40642a3e4461f35503241d5

Lendo da esquerda: 769 significa TLS 1.0; depois vêm doze suítes de cifra, três extensões, três curvas e um único formato de ponto. Aqui o MD5 não serve para segurança, e sim para transformar uma string longa em uma chave pesquisável de tamanho fixo.

Duas observações: como clientes de TLS 1.3 escrevem o valor do TLS 1.2 no campo de versão, quase toda string JA3 atual começa com 771. A Salesforce arquivou o repositório em 1 de maio de 2025; ferramentas como o Wireshark continuam calculando o valor, mas o método não recebe mais manutenção.

O que é GREASE e por que o JA3 o ignora?

GREASE é um mecanismo de robustez definido na RFC 8701. O cliente acrescenta às suas listas de suítes de cifra, extensões e grupos alguns valores reservados escolhidos ao acaso, no padrão 0x0A0A, 0x1A1A0xFAFA. Esses valores não significam nada. O objetivo é testar sem parar se os servidores ignoram em silêncio valores que não reconhecem: um servidor defeituoso que derruba a conexão diante de um valor desconhecido é notado hoje, e não no dia em que um recurso novo do TLS entrar no ar.

Como os valores são sorteados, o mesmo navegador produziria um hash diferente a cada conexão se o JA3 os contasse. Por isso a documentação do método pede que os valores GREASE sejam pulados; no código abaixo, quem faz isso é a função is_grease.

Por que o Chrome embaralha a ordem das extensões?

O GREASE protege o conteúdo das listas, não a ordem delas. O fato de softwares de servidor e de equipamentos intermediários confiarem na ordem fixa de extensões do Chrome trazia o mesmo risco, então o time do Chrome tornou a ordem aleatória também. Segundo o registro no Chrome Platform Status, o recurso «TLS ClientHello extension permutation» passou a vir ligado por padrão no Chrome 110. A justificativa registrada não é fugir do fingerprint, e sim evitar que o ecossistema fique frágil: uma ordem fixa leva quem desenvolve servidores a reconhecer o Chrome e supor um comportamento específico, o que dificulta mudanças futuras no TLS. A RFC 8446 já diz que as extensões podem vir em qualquer ordem; a única exceção é a pre_shared_key, que precisa ficar por último quando está presente.

Como o JA3 junta as extensões na ordem da mensagem, essa mudança deixou instável o hash JA3 dos navegadores baseados em Chromium. Medimos com o ouvinte mais abaixo: 24 conexões seguidas de um navegador baseado em Chromium deram 24 hashes JA3 diferentes. Ao ordenar os números das extensões, todas tinham o mesmo conjunto; só a ordem mudava.

O que é o JA4 e qual a diferença para o JA3?

O JA4 é o formato mais novo, publicado pela FoxIO, que responde a esse problema. Segundo a especificação técnica do JA4, o fingerprint tem três partes no formato a_b_c. O exemplo do documento, t13d1516h2_8daaf6152771_e5627efa2ab1, se lê assim:

  • t: TLS sobre TCP (q seria QUIC e d, DTLS).
  • 13: TLS 1.3. O JA4 lê a versão na extensão supported_versions, não no campo antigo.
  • d: existe extensão SNI, ou seja, o cliente está se conectando a um nome de domínio (i mostra que não há SNI).
  • 15 e 16: 15 suítes de cifra e 16 extensões, sem contar GREASE.
  • h2: o primeiro e o último caractere do primeiro valor da lista ALPN, isto é, HTTP/2.
  • 8daaf6152771: os códigos hexadecimais das suítes de cifra são ordenados, passam por SHA-256 e ficam os 12 primeiros caracteres.
  • e5627efa2ab1: os códigos de extensão são ordenados (sem SNI e ALPN), os algoritmos de assinatura são anexados na ordem original e o resultado é resumido do mesmo jeito.

A ordenação tira o efeito do embaralhamento das extensões. A primeira parte, legível, permite uma separação grosseira sem olhar os hashes: duas primeiras partes, uma terminando em h2 e a outra em 00 por não enviar ALPN, não vêm do mesmo software.

JA3JA4
Quem publicaSalesforce (2017), repositório arquivado em 2025FoxIO, com desenvolvimento ativo
Formatoum único hash MD5 de 32 caracteresa_b_c: prefixo legível + dois hashes SHA-256 encurtados
Ordem das extensõesa da mensagemordenada; não sofre com o embaralhamento
Versão do TLScampo de versão antigo (771 mesmo no TLS 1.3)extensão supported_versions
ALPN, SNIaparecem só como número de extensãoos dois vêm escritos na primeira parte
Algoritmos de assinaturanão usadosentram na terceira parte
GREASEignoradoignorado

Segundo a nota de licença no repositório da FoxIO, o JA4, o fingerprint de cliente TLS, é aberto sob licença BSD 3-Clause; os demais membros da família (JA4S, JA4H, JA4X, JA4T e por aí vai) seguem uma licença própria da FoxIO.

Um proxy muda o fingerprint TLS?

Não. O motivo está em como um proxy transporta o tráfego HTTPS.

No proxy HTTP, o cliente primeiro envia ao proxy uma requisição CONNECT destino.com:443. O proxy abre uma conexão TCP até o destino, responde 200 Connection Established e a partir daí só repassa bytes. O ClientHello viaja dentro desse túnel: quem o produz é o seu cliente, quem o lê é o site de destino, e o proxy não muda o conteúdo nem o reescreve. No SOCKS5 é igual; o protocolo repassa a conexão TCP uma camada abaixo e nem sabe que os dados que carrega são TLS. Percorremos os dois protocolos passo a passo em Diferença entre SOCKS e HTTP proxy.

O resultado é que o site vê ao mesmo tempo o endereço IP do proxy e o fingerprint TLS do seu cliente. Usar Proxies HTTPS ou Proxies SOCKS5 não muda isso, e o proxy ser residencial, móvel ou de datacenter também não. Testamos com um proxy de teste local: o mesmo cliente curl se conectou ao ouvinte abaixo de forma direta, por um túnel HTTP CONNECT e por SOCKS5, e o ouvinte registrou a mesma string JA3 nas três vezes.

O único caso em que o fingerprint muda é quando há no meio um intermediário que encerra o TLS:

O que está no meioQuem estabelece o TLS com o destino?Qual fingerprint o site vê?
Proxy HTTP (túnel CONNECT)Seu clienteO do seu cliente
Proxy SOCKS5Seu clienteO do seu cliente
VPNSeu clienteO do seu cliente
Gateway corporativo com inspeção TLSO gatewayO do gateway
Antivírus com varredura HTTPS ligadaO antivírusO do antivírus
Serviço que baixa a página no seu lugarO cliente do serviçoO do cliente do serviço

A quinta linha aconteceu na nossa própria máquina enquanto preparávamos este texto: o curl no Windows devolvia um fingerprint ao se conectar direto ao serviço de eco e outro ao passar pelo túnel do proxy local. A causa era a varredura HTTPS do antivírus, que refazia a conexão direta com a pilha TLS dele. Se o valor no serviço de eco não parece com a biblioteca que você esperava, veja quem assinou o certificado.

Como ver a sua própria string JA3?

O caminho mais curto é um serviço de eco: abra a página TLS do BrowserLeaks no navegador e você verá seus valores JA3 e JA4. Se quiser olhar no nível de pacote, o Wireshark calcula esses valores sozinho nos pacotes ClientHello; na referência de filtros de exibição os campos aparecem como tls.handshake.ja3 e tls.handshake.ja4.

Para ver o cálculo em si, você pode usar o script abaixo. Ele roda só com a biblioteca padrão do Python e não envia nada para fora: abre um ouvinte TCP cru na porta 8443, analisa a mensagem ClientHello de cada cliente que se conecta e imprime a string JA3 e o hash dela. Na inicialização ele conecta uma vez o próprio cliente urllib do Python. O ouvinte não completa o handshake, lê o primeiro pacote e fecha; um erro de conexão do lado do cliente é o esperado.

python
import hashlib
import socket
import threading
import urllib.request

HOST, PORT = "127.0.0.1", 8443


def is_grease(value):
    # RFC 8701: 0x0a0a, 0x1a1a, ... 0xfafa
    return (value & 0x0F0F) == 0x0A0A and (value >> 8) == (value & 0xFF)


def read_client_hello(conn):
    data = b""
    while len(data) < 5:
        data += conn.recv(4096)
    if data[0] != 22:  # 22 = registro de handshake
        raise ValueError("nao e um handshake TLS")
    record_length = int.from_bytes(data[3:5], "big")
    while len(data) < 5 + record_length:
        chunk = conn.recv(4096)
        if not chunk:
            break
        data += chunk
    return data[5 : 5 + record_length]


def ja3_from_client_hello(hello):
    if hello[0] != 1:  # 1 = ClientHello
        raise ValueError("nao e um ClientHello")
    pos = 4  # tipo da mensagem (1) + tamanho (3)
    version = int.from_bytes(hello[pos : pos + 2], "big")
    pos += 2 + 32  # versao + random
    pos += 1 + hello[pos]  # session_id
    size = int.from_bytes(hello[pos : pos + 2], "big")
    pos += 2
    ciphers = [int.from_bytes(hello[i : i + 2], "big") for i in range(pos, pos + size, 2)]
    pos += size
    pos += 1 + hello[pos]  # compression_methods
    end = pos + 2 + int.from_bytes(hello[pos : pos + 2], "big")
    pos += 2

    extensions, groups, point_formats = [], [], []
    while pos < end:
        ext_type = int.from_bytes(hello[pos : pos + 2], "big")
        ext_size = int.from_bytes(hello[pos + 2 : pos + 4], "big")
        body = hello[pos + 4 : pos + 4 + ext_size]
        pos += 4 + ext_size
        extensions.append(ext_type)
        if ext_type == 10:  # supported_groups
            groups = [int.from_bytes(body[i : i + 2], "big") for i in range(2, len(body), 2)]
        elif ext_type == 11:  # ec_point_formats
            point_formats = list(body[1:])

    def join(values):
        return "-".join(str(v) for v in values if not is_grease(v))

    fields = [str(version), join(ciphers), join(extensions), join(groups), join(point_formats)]
    ja3_text = ",".join(fields)
    return ja3_text, hashlib.md5(ja3_text.encode()).hexdigest()


def own_python_client():
    try:
        urllib.request.urlopen(f"https://localhost:{PORT}", timeout=5)
    except OSError:
        pass  # o ouvinte fecha sem responder, e o esperado


def main():
    server = socket.create_server((HOST, PORT))
    print(f"ouvindo em https://localhost:{PORT}, Ctrl+C para sair")
    threading.Thread(target=own_python_client, daemon=True).start()
    while True:
        conn, _ = server.accept()
        with conn:
            try:
                ja3_text, ja3_hash = ja3_from_client_hello(read_client_hello(conn))
            except (ValueError, IndexError, OSError) as exc:
                print("nao foi possivel ler:", exc)
                continue
        print(ja3_hash, ja3_text)


if __name__ == "__main__":
    main()

Com o script rodando, aponte clientes diferentes para o mesmo endereço a partir de outro terminal. Escreva localhost no endereço; um cliente que se conecta a um endereço IP não envia a extensão SNI e o fingerprint muda.

bash
curl -k https://localhost:8443
node -e "require('https').get('https://localhost:8443', { rejectUnauthorized: false }).on('error', () => {})"

Na nossa máquina (Windows 11, Python 3.13 com OpenSSL 3.0, Node.js 24 e curl 8 compilado com Schannel) a saída foi esta. Encurtamos o campo das suítes de cifra para melhorar a leitura:

text
331a436afb23d4e31134c11b301bdcb5 771,4866-4867-4865-…,0-11-10-35-16-22-23-49-13-43-45-51-21,29-23-30-25-24-256-257-258-259-260,0-1-2
2e6c64f66822fc35b6a7a128b557f1de 771,4866-4865-49196-…,0-43-13-35-10-11-16-51-49-23-65281-45,29-23-24,0
944d1e1858cd278718f8a46b65d3212f 771,4866-4867-4865-…,65281-0-11-10-35-22-23-13-43-45-51,4588-29-23-30-24-25-256-257,0-1-2

A mesma máquina, o mesmo IP e três identidades distintas: na ordem, Python, curl e Node.js. Para o Python, o serviço de eco devolveu exatamente o mesmo hash, ou seja, o analisador funciona. O 4588 na linha do Node.js é o grupo híbrido pós-quântico de troca de chaves que aparece nos registros da IANA como X25519MLKEM768; esta versão do Python não o envia. No mesmo Node.js, https.get e o fetch embutido também deram valores JA3 diferentes, porque o fetch acrescenta a extensão ALPN: o fingerprint depende da biblioteca HTTP usada, não da linguagem. As diferenças entre bibliotecas do lado do Python nós comparamos em Comparativo entre HTTPX, Requests e AIOHTTP.

Para medir o efeito de um proxy o ouvinte não serve (um proxy remoto não alcança o seu localhost), então pergunte duas vezes ao serviço de eco:

python
import json
import urllib.request

PROXY = "http://user:pass@pr.proxynet.io:8000"
URL = "https://tls.browserleaks.com/json"


def fingerprint(opener):
    with opener.open(URL, timeout=15) as response:
        result = json.load(response)
    return result["ja3_hash"], result["ja4"]


direct = urllib.request.build_opener(urllib.request.ProxyHandler({}))
proxied = urllib.request.build_opener(urllib.request.ProxyHandler({"https": PROXY}))

print("direto:", *fingerprint(direct))
print("proxy :", *fingerprint(proxied))

Você verá os mesmos valores nas duas linhas; o que muda é só o endereço IP que o serviço enxerga.

Como o dono de um site usa esse dado?

Produtos de CDN e firewall expõem o valor JA3 ou JA4 como um campo ao lado de cada requisição. O dono do site usa esse campo ao escrever regras e ao revisar os registros. Usos típicos:

  • Verificação de coerência. Se o User-Agent diz «Chrome» mas o fingerprint não bate com nenhuma versão conhecida do Chrome, o cabeçalho foi alterado. Sozinho não é prova definitiva, mas é um sinal forte que pesa na pontuação. O cabeçalho em si está em O que é User-Agent?.
  • Limite de requisições independente do IP. Se um tráfego espalhado por centenas de IPs carrega o mesmo fingerprint, o contador pode ser amarrado ao fingerprint em vez do IP. Um IP rotativo não zera esse contador.
  • Reconhecer ferramentas conhecidas. Existem listas de fingerprints compiladas para famílias de malware e ferramentas de varredura; times de segurança as cruzam com os registros.

Também há limites. Um site que bloqueia o valor de um navegador atual bloqueia todos os visitantes que usam esse navegador. Atualizações do navegador mudam o valor, e gateways corporativos e antivírus colocam os fingerprints deles no meio. Por isso um fingerprint TLS não decide nada sozinho; é um dos sinais que alimentam a pontuação junto da reputação do IP e do comportamento. Essa pontuação como um todo está em Como funciona a detecção de bots?, e as camadas da Cloudflare em O que é o Cloudflare Precursor?.

O que isso significa para quem coleta dados?

Voltando à situação da introdução. O cliente Python diz na linha User-Agent que é Chrome, enquanto o ClientHello dele diz que é OpenSSL. O site vê a contradição no primeiro pacote. A reação certa é eliminar a contradição, não tentar escondê-la:

  • Não diga que é um navegador que você não é. Deixe o User-Agent do seu script apresentar o script: um nome, uma versão e um endereço de contato. Um cliente que chega com identidade honesta, respeita o robots.txt e trabalha devagar pode ser reconhecido pelo dono do site e entrar numa lista de permissões. As regras estão em O que é robots.txt e como ler o arquivo?.
  • Se existe uma API oficial, use. Uma requisição que chega com chave de API já tem identidade conhecida; a discussão sobre fingerprint some.
  • Se a página realmente precisa de um navegador, use um de verdade. Quando você abre uma página desenhada por JavaScript com o Playwright dentro de um Chromium real, o seu cliente é mesmo aquele navegador; cabeçalhos, camada TLS e ambiente JavaScript concordam entre si. A instalação está em O que é Playwright e como usá-lo com proxy.
  • Reduza o ritmo e peça permissão. Para um trabalho regular e de grande volume, escrever ao dono do site costuma ser a solução mais duradoura.

Os outros motivos de bloqueio, e os caminhos legítimos para eles, estão reunidos em Como fazer web scraping sem ser bloqueado.

Casos de uso

  • Diagnosticar um 403 no scraper: se com o mesmo IP o navegador passa e o script não, a diferença está quase certamente na identidade do cliente. Os códigos de status separamos em Códigos de status HTTP no web scraping, e o desenho geral está na nossa página de solução de extração de dados.
  • Separar o tráfego de bots no seu próprio site: agrupar os registros por fingerprint mostra o tráfego distribuído que o agrupamento por IP deixa passar.
  • Apresentar o seu próprio crawler: uma versão fixa de biblioteca produz um fingerprint constante, e é assim que o seu crawler é reconhecido nos registros. A parte de infraestrutura está na nossa página de solução de web crawler.
  • Acertar a expectativa sobre um proxy: Proxies residenciais muda a reputação do IP e a localização; a identidade do seu cliente continua sendo responsabilidade sua.

Erros comuns

  • Achar que o cliente muda quando o User-Agent muda. O cabeçalho é texto; o ClientHello é o comportamento da biblioteca e sai antes do cabeçalho.
  • Esperar que o proxy mude o fingerprint TLS. Um proxy que faz túnel muda o IP e não encosta no handshake.
  • Esperar que o hash JA3 fique fixo em um navegador baseado em Chromium. A ordem das extensões muda a cada conexão; para comparar, use JA4 ou uma lista de extensões ordenada.
  • Aceitar o resultado do serviço de eco sem questionar. Um antivírus ou gateway corporativo que varre HTTPS mostra ao serviço o fingerprint dele mesmo.
  • Usar 127.0.0.1 na medição. Não vai SNI, e o valor sai diferente do das conexões reais.

Guia de decisão

SituaçãoRecomendação
O IP muda pelo proxy e mesmo assim vem 403 na primeira requisiçãoOlhe a identidade do cliente: o User-Agent e a biblioteca que você usa dizem a mesma coisa?
Você quer saber o valor JA3 ou JA4 do seu próprio clienteUm serviço de eco ou o ouvinte local acima
Você quer agrupar o tráfego do Chrome nos seus registrosJA4, não JA3; não sofre com o embaralhamento de extensões
A página precisa de JavaScript e de um navegador realAutomação com navegador real (Playwright), ritmo razoável e o escopo que o site permite
O serviço de eco mostra um valor inesperadoVeja quem assinou o certificado; pode haver software encerrando o TLS no meio
Seu site recebe tráfego de bots distribuídoAmarre o limite de requisições ao par IP + fingerprint em vez do IP; não bloqueie por um único hash

Perguntas frequentes

Qual a diferença entre JA3 e JA3S?

O JA3 é produzido a partir da mensagem ClientHello do cliente, e o JA3S a partir da mensagem ServerHello do servidor. Como um servidor responde de forma diferente a clientes diferentes, o JA3S não identifica o servidor sozinho; já a resposta dele ao mesmo cliente é sempre a mesma. Por isso times de segurança usam os dois em par.

Usar uma VPN muda o fingerprint TLS?

Não. A VPN leva o tráfego por um túnel criptografado, mas o handshake TLS com o site continua sendo feito pelo seu navegador ou pelo seu script. O site vê o endereço IP do servidor VPN e o fingerprint do seu cliente. A diferença entre as duas ferramentas está em Diferença entre proxy e VPN.

Um fingerprint TLS me identifica pessoalmente?

Sozinho, não. Todo mundo que roda a mesma versão de navegador no mesmo sistema operacional produz o mesmo valor. Uma identidade que chega perto de uma pessoa nasce da combinação desse valor com o endereço IP, os cookies e os sinais da camada de JavaScript.

Atualizar o navegador muda o fingerprint?

Pode mudar. Quando uma versão nova remove uma suíte de cifra ou acrescenta uma extensão, a lista e o hash mudam. Por isso as listas de fingerprints são mantidas versão a versão.

Uma janela anônima ou apagar os cookies afeta o fingerprint TLS?

Não. A mensagem ClientHello é produzida pela biblioteca TLS do navegador; histórico, cookies e tipo de janela não entram nessas listas.

O que faço se estou sendo bloqueado por causa do meu fingerprint?

Meça primeiro: veja qual valor aparece no serviço de eco e se há software encerrando o TLS no meio. Se você escreve um script, declare a sua identidade com honestidade, reduza o ritmo e use a API oficial ou o canal de permissão do site. Se o bloqueio acontece com um navegador comum, o problema quase certamente está na reputação do IP, e não no fingerprint.

Em resumo

O fingerprint TLS sai do ClientHello, o primeiro pacote, sem criptografia, de uma conexão criptografada. O JA3 junta na ordem as cinco listas dessa mensagem e tira o hash MD5; quando o Chrome passou a embaralhar a ordem das extensões, o hash ficou instável, e o JA4 resolveu isso ordenando as listas. O valor mostra o software, não a pessoa, sai antes dos cabeçalhos e nenhum proxy que faça túnel encosta nele: o proxy muda o IP e a identidade do cliente fica com você. Para somar o tipo de IP certo a uma identidade de cliente constante e honesta, dê uma olhada nos nossos serviços de proxy.