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

Как антифрод TikTok связывает аккаунты по TLS JA3/JA4 при работе через разные прокси

Как антифрод TikTok связывает аккаунты по TLS JA3/JA4 при работе через разные прокси

Прокси меняет IP. Это все знают. Меньше людей в курсе, что TikTok смотрит не только на IP, но и на то, как именно ваш клиент устанавливает TLS-соединение. Отпечаток TLS — это метка, которую вы тащите за собой через все прокси, даже если каждый раз берёте новый адрес. Разберём, как это работает и почему смена прокси не спасает от связывания.

Что такое JA3 и JA4

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

Что входит в строку:

- версия TLS (например, 771 — это TLS 1.2)

- список cipher suites в порядке следования

- список extensions

- список elliptic curves

- формат точек эллиптических кривых

JA4 — следующее поколение, появился в 2023. Он умнее: учитывает, что клиент может поддерживать QUIC, разделяет отпечатки по типу транспорта, не так сильно зависит от порядка некоторых полей. Формат выглядит как `t13d1516h2_8daaf6152771_b186095e22b6` — три части через подчёркивание.

TikTok парсит оба. Если у вас старый клиент или кастомная сборка curl — вы попадёте в JA3. Если свежий Chrome — попадёте в JA4-категорию, но JA3 всё равно посчитают для совместимости.

Почему IP — не единственный маркер

Классическая логика антифрода: если два аккаунта заходят с одного IP — они связаны. Прокси ломает эту связь. Но остаётся куча других сигналов, и TLS — один из самых устойчивых.

Пример: у вас десять аккаунтов, десять прокси. Каждый раз новый IP, новая геолокация, чистые куки. Но все десять заходят через одну и ту же сборку Chromium с одинаковым набором cipher suites. JA3 у всех идентичен. Антифрод складывает это в граф: узел «JA3 = abc123...» соединён с десятью аккаунтами. Всё, вы в одной группе.

Причём это работает даже если IP разные, часовые пояса разные, языки разные. Отпечаток TLS не зависит от прокси. Прокси работает на уровне TCP, а JA3 формируется выше — на уровне TLS-рукопожатия. Прокси видит только зашифрованный поток и не может его подменить, если не делает MITM.

Как TikTok собирает отпечаток

TikTok использует несколько слоёв сбора. Первый — на уровне CDN и edge-серверов. Когда ваш клиент приходит на `*.tiktokcdn.com` или `*.tiktokv.com`, TLS-терминация происходит на балансировщике. Он логирует ClientHello целиком, включая JA3 и JA4.

Второй слой — внутри приложения. Мобильный клиент TikTok отправляет на сервер кучу телеметрии: `device_id`, `iid`, `openudid`, версию WebView, набор поддерживаемых шифров на уровне приложения (там свой TLS-стек). Это уже не JA3 в чистом виде, но сервер может сопоставить: «клиент заявил Chrome 120, а JA3 у него от Chrome 118». Расхождение — красный флаг.

Третий слой — поведенческий. Но он нас сейчас не интересует.

Собранные отпечатки складываются в профиль. Если с одного JA3 приходит 50 регистраций за сутки — это фрод-паттерн. Если с одного JA3 приходит один аккаунт годами — норма.

Что реально отличается между клиентами

Разные клиенты дают разные JA3. Вот реальные значения для популярных стеков (упрощённо, только для иллюстрации):

| Клиент | JA3 (MD5) | JA4 |

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

| Chrome 120 (Win) | `cd08e31494f9531f560d64c695473da9` | `t13d1516h2_8daaf6152771_b186095e22b6` |

| Firefox 121 | `579ccef312d18482fc42e2b822ca2430` | `t13d1715h2_5b57614c22b0_3cbfd9057e0d` |

| curl 8.5 (OpenSSL 3.0) | `3b5074b1b5d032e5620f69f9f700ff0e` | `t13d3112h2_e8f1e7e78f70_9b8f3e9d6c3a` |

| Python requests 2.31 | `3b5074b1b5d032e5620f69f9f700ff0e` | `t13d3112h2_e8f1e7e78f70_9b8f3e9d6c3a` |

| Go net/http 1.21 | `9e10a8b1e9c3b4a3c4e5f6a7b8c9d0e1` | `t13d2210h2_...` |

Заметьте: curl и Python requests дают одинаковый JA3, потому что оба используют OpenSSL с дефолтными настройками. Это классическая ловушка. Гоняете парсер на Python через ротацию прокси — а антифрод видит один и тот же отпечаток на всех запросах.

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

Прокси на уровне SOCKS5 или HTTP CONNECT просто туннелирует TCP-поток. TLS-рукопожатие идёт между вашим клиентом и сервером TikTok напрямую. Прокси видит только байты, но не может их изменить — иначе сломается шифрование.

Единственный способ подменить JA3 через прокси — MITM-прокси с собственным сертификатом. Тогда прокси терминирует TLS, читает ClientHello, подменяет его и устанавливает новое соединение с сервером. Но для этого нужен доверенный сертификат на клиенте. Для мобильного TikTok это невозможно — приложение пиннит сертификаты.

Итог: какой бы прокси вы ни взяли, JA3 вашего клиента уедет на сервер как есть.

Как проверить свой отпечаток

Самый простой способ — отправить запрос на сервис, который показывает ваш JA3/JA4. Например, `tls.peet.ws` или `ja3er.com`.

```bash

curl -s https://tls.peet.ws/api/all | jq '.tls.ja3, .tls.ja4'

```

Для Python:

```python

import requests

r = requests.get("https://tls.peet.ws/api/all")

data = r.json()

print("JA3:", data["tls"]["ja3"])

print("JA3 hash:", data["tls"]["ja3_hash"])

print("JA4:", data["tls"]["ja4"])

print("Akamai fingerprint:", data["http2"]["akamai_fingerprint"])

```

Запустите это через разные прокси. Отпечаток не изменится. Запустите с разных машин с разными версиями Python — тоже, скорее всего, не изменится, потому что OpenSSL один и тот же.

Как менять отпечаток

Есть несколько путей.

Первый — использовать curl-impersonate или его форки. Это патченный curl, который воспроизводит TLS-рукопожатие Chrome или Firefox один в один. JA3 совпадает с реальным браузером.

```bash

curl_chrome120 -s https://tls.peet.ws/api/all | jq '.tls.ja4'

```

Второй — библиотеки вроде `curl_cffi` для Python. Она оборачивает curl-impersonate и даёт питонический API.

```python

from curl_cffi import requests

r = requests.get(

"https://tls.peet.ws/api/all",

impersonate="chrome120",

proxies={"https": "socks5://user:pass@proxy.example:1080"}

)

print(r.json()["tls"]["ja4"])

```

Третий — собственный TLS-стек на Go или Rust с настраиваемым ClientHello. Это уже для параноиков, но даёт полный контроль.

Четвёртый — использовать реальные браузеры через Playwright или Puppeteer. Тогда JA3 будет настоящим, но вы получите все остальные отпечатки браузера тоже — canvas, WebGL, шрифты. Что тоже неплохо, если задача — выглядеть как живой пользователь.

Где тут место для прокси

Прокси нужны для смены IP. Это базовая гигиена. Но если вы гоняете один и тот же Python-скрипт через сто прокси — антифрод свяжет все сто сессий по JA3 за пять минут.

Правильная схема: разные клиенты с разными отпечатками + ротация прокси. Тогда каждый запрос выглядит как отдельный пользователь. Сервисы вроде lexic.ml дают IPv6-пулы, где каждый адрес — отдельная /128, и это хорошо ложится на схему «один отпечаток — один IP — один аккаунт».

Но IPv6 сам по себе не спасёт. Если у вас один JA3 на весь пул — связывание всё равно произойдёт. Отпечаток и IP должны меняться вместе.

Реальный кейс: парсер TikTok Shop

Команда делала мониторинг цен на TikTok Shop. Сто аккаунтов, сто резидентских прокси, ротация по кругу. Через три дня — бан 80% аккаунтов.

Причина: все запросы шли через `requests` с дефолтным OpenSSL. JA3 одинаковый. Плюс HTTP/2 fingerprint тоже одинаковый — а TikTok логирует и его. Akamai fingerprint (порядок заголовков, приоритеты потоков) совпадал до байта.

Решение: переписали на `curl_cffi` с `impersonate="chrome120"`. Отпечатки стали разные — рандомизировали между Chrome 118, 119, 120, Firefox 120, Safari 17. Плюс добавили рандомные задержки между запросами и разные User-Agent, синхронизированные с JA3. Баны упали до 5% в неделю.

Кейс: мобильный TikTok через эмулятор

Другой пример. Ребята гоняли мобильный TikTok через эмуляторы Android. Каждый эмулятор — свежий `device_id`, свежий прокси. Но TLS-стек внутри Android WebView одинаковый у всех эмуляторов одной версии.

TikTok видел: сто аккаунтов, сто device_id, но один JA4. Плюс один и тот же `iid` в телеметрии (потому что эмулятор клонировали). Связывание произошло мгновенно. Плюс бан по железу — эмуляторы палились по другим маркерам.

Решение: разные версии Android на разных эмуляторах, разные версии WebView, рандомизация `iid`, плюс прокси с IPv6. JA4 разошёлся. Но полностью проблему не убрало — мобильный антифрод TikTok копает глубже, чем TLS.

Кейс: API TikTok через серверные прокси

Тут интереснее. Компания дёргала TikTok API для аналитики. Запросы шли с серверных IP, через датацентровые прокси. IP чистые, но все из одной подсети /24.

JA3 одинаковый — Go net/http. JA4 тоже. Плюс HTTP/2-фреймы идентичные. TikTok сгруппировал все запросы в один кластер и прикрутил rate limit на весь кластер.

Решение: переписали клиент на Rust с `reqwest` и кастомным TLS-стеком (`rustls` с настраиваемым ClientHello). Плюс раскидали по разным /24 и /48. Плюс разные версии TLS — где-то 1.2, где-то 1.3. Rate limit перестал срабатывать на весь пул.

Что ещё смотрит TikTok

JA3/JA4 — не единственное. Есть HTTP/2 fingerprint (набор SETTINGS-фреймов, порядок заголовков, WINDOW_UPDATE). Есть TCP fingerprint (TTL, размер окна, опции). Есть DNS-резолвер, к которому обращается клиент. Есть timing — задержки между пакетами в рукопожатии.

Всё это складывается в один большой профиль. JA3 — просто самый удобный маркер, потому что он стабильный и легко считается.

Если хотите реально развести аккаунты — меняйте весь стек: TLS, HTTP/2, TCP, DNS, timing. Прокси меняет только IP. Это necessary, но не sufficient.

Практические выводы

Первое. Никогда не гоняйте один и тот же клиент через ротацию прокси. Отпечаток уедет один и тот же.

Второе. Используйте инструменты с подменой отпечатка: curl-impersonate, curl_cffi, Playwright. Они дают реалистичные JA3/JA4.

Третье. Синхронизируйте User-Agent с отпечатком. Если JA3 от Chrome 120, а UA от Chrome 118 — это палево.

Четвёртое. Меняйте отпечатки вместе с IP. Один отпечаток — один IP — один аккаунт. Иначе граф свяжет всё в кучу.

Пятое. Проверяйте себя. `tls.peet.ws` покажет JA3, JA4, Akamai fingerprint. Прогоните через него свой стек и посмотрите, на что вы похожи.

TikTok не дураки. Они собирают всё, что можно собрать. Прокси — это только первый слой. Кто останавливается на нём, тот попадает в бан через неделю и не понимает почему.

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