← Назад в базу знаний

TLS-фингерпринт браузера: почему прокси не спасает от детекта по JA3/JA4

TLS-фингерпринт браузера: почему прокси не спасает от детекта по JA3/JA4

Что такое JA3 и откуда он взялся

Salesforce в 2017 году предложила способ фингерпринтинга TLS-клиентов без единого байта полезной нагрузки. Идея простая: взять поля ClientHello, склеить их в строку через запятую, посчитать MD5. Полученный хеш — это JA3. Поля такие: версия TLS, список шифров, список расширений, список эллиптических кривых, форматы точек эллиптических кривых.

Никаких сертификатов, никаких HTTP-заголовков. Только то, что клиент говорит в первом пакете рукопожатия.

```python

import hashlib

def ja3_string(version, ciphers, extensions, curves, ec_formats):

return ",".join([

str(version),

"-".join(map(str, ciphers)),

"-".join(map(str, extensions)),

"-".join(map(str, curves)),

"-".join(map(str, ec_formats)),

])

def ja3_hash(version, ciphers, extensions, curves, ec_formats):

s = ja3_string(version, ciphers, extensions, curves, ec_formats)

return hashlib.md5(s.encode()).hexdigest()

```

Chrome 120 на Windows даёт один хеш, Chrome 120 на Android — другой, curl — третий, python-requests — четвёртый. Причём стабильно: обновил Chrome — хеш сменился, откатил — вернулся. Это делает JA3 удобным сигналом для WAF и антифрода. Cloudflare, Akamai, DataDome, PerimeterX — все считают его на входе.

Почему прокси тут не помогает

Прокси меняет IP. Иногда — страну, ASN, репутацию адреса. Но TLS-рукопожатие устанавливает клиент, а не прокси. Если вы используете HTTP-прокси (CONNECT-туннель), TLS идёт end-to-end между вашим curl и целевым сервером. Прокси видит только «клиент хочет CONNECT к example.com:443», дальше — поток байт.

SOCKS5 — то же самое. Проксирует TCP-поток, не трогает содержимое. Значит, ClientHello уходит от вашего настоящего стека. curl 8.5 на Ubuntu 22.04 имеет свой JA3, и он не совпадает с Chrome. Прокси это не лечит.

Только если прокси делает TLS-терминацию (MITM), тогда он может подменить ClientHello. Но тогда у вас проблемы с сертификатами, и большинство антифрод-систем это тоже ловят — по несоответствию JA3 и User-Agent, по issuer сертификата, по SNI.

JA4: что изменилось в 2023

FoxIO в 2023 выпустила JA4 — переработку идеи. Три ключевых изменения. Первое: вместо MD5 — усечённый SHA256, чтобы снизить коллизии (в JA3 они реально случались). Второе: хеш разбит на читаемые части — версия протокола, число шифров, число расширений, ALPN. Третье: добавлены сигналы, которые JA3 игнорировал — порядок расширений, наличие SNI, поддерживаемые версии TLS в extension 43.

Формат JA4 выглядит так: `t13d1516h2_8daaf6152771_b186095e22b6`. Первая часть — метаданные, вторая — хеш шифров и расширений, третья — хеш сигнатуры. У Chrome 120 это `t13d1516h2_8daaf6152771_b186095e22b6`, у Firefox 121 — `t13d1715h2_5b57614c22b0_6a4b1e3b0f2c`. Разница видна невооружённым глазом.

JA4+ включает ещё JA4H (HTTP-заголовки), JA4S (серверный ответ), JA4X (сертификаты). Вместе они дают полный отпечаток сессии.

Как выглядит детект на практике

WAF получает ClientHello, считает JA4, сравнивает с базой известных клиентов. Если JA4 = Chrome, а User-Agent = python-requests/2.31 — срабатывает правило. Если JA4 не встречался вообще никогда — срабатывает другое правило, «неизвестный клиент». Если JA4 меняется между запросами одной сессии — третье правило.

```bash

curl -s --tlsv1.3 --tls-max 1.3 https://tls.peet.ws/api/all | jq '.tls.ja3_hash, .tls.ja4'

```

Запустите это с curl и с Chrome — увидите разные хеши. С curl на macOS и curl на Ubuntu — тоже разные, потому что сборки линкуются с разными версиями OpenSSL/BoringSSL/LibreSSL.

Реальный сценарий: парсинг маркетплейса

Задача: собирать цены с крупного маркетплейса. 50 000 запросов в сутки. Ротация прокси — 2000 IP, residential. Казалось бы, всё правильно.

Первая неделя — 98% успеха. Вторая — 40%. Третья — 5%, все запросы отдают 403 с одинаковым телом.

Причина: маркетплейс внедрил JA4-детект. Все 50 000 запросов шли с одним и тем же JA4 — от python-requests 2.31 через urllib3. Прокси менялись, IP разные, ASN разные — но TLS-отпечаток один. Система связала все сессии в одну и забанила паттерн.

Решение: подмена JA3/JA4 на уровне TLS-стека. Библиотеки вроде `curl_cffi` (Python) умеют эмулировать отпечатки Chrome, Firefox, Safari. Внутри — патченный BoringSSL, который формирует ClientHello побайтово как настоящий браузер.

```python

from curl_cffi import requests

r = requests.get(

"https://example.com/api/prices",

impersonate="chrome120",

proxies={"https": "http://user:pass@proxy.example:8080"},

timeout=15,

)

print(r.status_code, r.json())

```

После перехода успех вернулся к 90%+. Прокси остались те же.

Почему эмуляция — не серебряная пуля

JA4 — не единственный сигнал. Есть HTTP/2 fingerprint (SETTINGS-фрейм, приоритеты, порядок псевдозаголовков). Есть порядок HTTP-заголовков. Есть TCP-фингерпринт (p0f). Есть тайминги между пакетами рукопожатия.

Если вы подменили JA4 на Chrome, но оставили HTTP/2-фрейм от Go — детект сработает на следующем слое. Поэтому `curl_cffi`, `tls-client` (Go), `cycletls` эмулируют всё вместе: TLS, HTTP/2, порядок заголовков.

Проверить, что вы спалились, можно так:

```bash

curl -s https://tls.browserleaks.com/json | jq

```

Сервис покажет JA3, JA4, HTTP/2 Akamai fingerprint, порядок заголовков. Сравните с реальным Chrome.

Таблица: что эмулируют популярные инструменты

| Инструмент | Язык | JA3/JA4 | HTTP/2 | Порядок заголовков |

|---|---|---|---|---|

| curl (vanilla) | C | нет | нет | нет |

| python-requests | Python | нет | нет | нет |

| curl_cffi | Python | да | да | да |

| tls-client | Go | да | да | да |

| cycletls | Go | да | да | частично |

| undici | Node | частично | да | нет |

Кейс с банком

Клиент делал агрегатор банковских выписок. Открывал сессии через residential-прокси, логинился в интернет-банк, забирал JSON. Банк внедрил JA4 + поведенческий анализ.

Первые два месяца — нормально. Потом массовые блокировки аккаунтов пользователей, чьи данные агрегировались. Банк видел: с аккаунта заходят два клиента — мобильное приложение (JA4 = iOS Safari) и агрегатор (JA4 = python). Сессия одна, отпечатки разные — флаг.

Прокси тут не при чём. Аккаунт один, IP может быть любой. Палит именно несоответствие отпечатков. Решение — эмулировать мобильное приложение на уровне TLS, включая специфичный для iOS набор шифров и порядок расширений. Это уже не просто «поставить curl_cffi», а разбирать ClientHello из mitmproxy-дампа приложения и воспроизводить его.

Как защититься от детекта по отпечатку

Если вы на стороне клиента — эмулируйте реальный браузер целиком, а не только TLS. Проверяйте себя через browserleaks, peet.ws, tlsfingerprint.io. Меняйте отпечаток не реже, чем раз в несколько часов, если работаете в серую.

Если вы на стороне сервиса — комбинируйте сигналы. JA4 сам по себе даёт ложные срабатывания: новые версии браузеров, редкие ОС, корпоративные прокси с TLS-инспекцией. Нужен контекст: совпадает ли JA4 с User-Agent, стабилен ли отпечаток в сессии, соответствует ли он геолокации IP.

Инфраструктура: где ставить JA4-детект

Классическая схема — на edge, до приложения. nginx сам JA4 не считает, но умеет отдавать ClientHello через `ssl_preread` в stream-модуле. Дальше парсите и считаете хеш:

```nginx

stream {

server {

listen 443;

ssl_preread on;

proxy_pass backend:8443;

proxy_set_header X-ClientHello \$ssl_preread_client_hello;

}

}

```

`\$ssl_preread_client_hello` — сырые байты ClientHello в hex. Парсите их в Lua или выносите в отдельный сервис. Для высоких нагрузок лучше держать детект на eBPF или на выделенном прокси-слое.

Прокси вроде lexic.ml решает задачу сетевого уровня — ротацию IPv6, распределение по ASN, обход гео-блокировок. Отпечаток TLS он не трогает и не должен. Это разные слои, и путать их — классические грабли: настроили 5000 прокси, а палитесь одним хешем.

Что дальше

JA4 постепенно вытесняет JA3 в новых внедрениях. Cloudflare включил JA4 в Bot Management в 2024. Akamai — в свою платформу. Крупные антифрод-вендоры мигрируют. Если вы строите парсинг или автоматизацию — закладывайте эмуляцию отпечатка сразу, а не когда упрётесь в 403.

Прокси — это про сеть. Отпечаток — про клиент. Работать должно вместе, иначе деньги на residential-пул уходят в трубу.

✔️Купить прокси