TLS-фингерпринт браузера: почему прокси не спасает от детекта по JA3/JA4
Содержание
- Что такое JA3 и откуда он взялся
- Почему прокси тут не помогает
- JA4: что изменилось в 2023
- Как выглядит детект на практике
- Реальный сценарий: парсинг маркетплейса
- Почему эмуляция — не серебряная пуля
- Таблица: что эмулируют популярные инструменты
- Кейс с банком
- Как защититься от детекта по отпечатку
- Инфраструктура: где ставить 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-пул уходят в трубу.