TLS-отпечатки и клиентские сертификаты

Управление TLS ClientHello и предъявление mTLS-сертификатов при replay.

Антибот-платформы оценивают TLS ClientHello ещё до прихода первых прикладных данных: наборы шифров, порядок расширений, ALPN и размещение GREASE складываются в хеши JA3/JA4, по которым фильтруется трафик. Поэтому replay с идеальными заголовками поверх стандартного TLS-стека всё равно проваливается. Replay в Traffic Jam восстанавливает ClientHello захваченного клиента — или профиль браузера/приложения из набора — через uTLS, сохраняет порядок заголовков из захвата и умеет предъявлять mTLS-клиентские сертификаты на любом из транспортов.

Транспорты replay

Каждый replay (откройте запрос в API-лаборатории → Replay) выбирает транспорт:

ТранспортClientHelloПорядок заголовков
native (по умолчанию) — нативный Go-транспортСобственный hello используемого Go-тулчейнаGo сортирует заголовки; порядок расходится с захватом
utls — точный по отпечатку (uTLS)Восстановлен из профиля (ниже)Порядок из захвата записывается байт в байт для HTTP/1.1

JA3/JA4 нативного транспорта вычисляется один раз за процесс на локальном слушателе с тем же набором ALPN. Транспорт uTLS устанавливает соединение с одноразовым спецификатором HelloCustom, собираемым заново для каждого соединения.

Выбор профиля ClientHello

В диалоге replay установите Transport в Fingerprint-faithful (uTLS), затем выберите профиль ClientHello:

  • capturedCaptured client (session fingerprint), значение по умолчанию для uTLS; восстанавливает hello из TLS-отпечатка захваченной сессии. Если в обмене нет захваченного отпечатка, диалог предупредит, а профиль упадёт при replay — выберите пресет.
  • golangGo (native stack): нативный hello Go, восстановленный через uTLS; контрольный вариант для A/B-проверки фильтрации по TLS-отпечатку.
  • Пресеты — реальные hello клиентов из uTLS: Chrome (133, 131, 120, 120-pq, 115-pq, 106, 100, 83, 70), Firefox (120, 105, 102, 99, 65, 55), Safari/iOS (16.0, 14, 13, 12.1, 11.1) и другие (android-11-okhttp, edge-85, 360-7.5, qq-11.1).

JA3/JA4 каждого профиля вычисляется из его спецификатора и отдаётся через GET /api/replay/transports.

Флажок Force HTTP/1.1 переписывает спецификатор так, чтобы в ALPN предлагался только http/1.1, и удаляет HTTP/2-расширение application_settings — тогда порядок заголовков из захвата сохраняется байт в байт.

Как бэкенд воспроизводит ClientHello

При сборке спецификатора из захваченного отпечатка сохраняется то, что наблюдают системы фингерпринтинга, и отбрасывается то, что свежая сессия передать не может:

  • Порядок расширений сохраняется — он входит в JA3 и JA4.
  • GREASE генерируется заново — значения создаются для каждого соединения; uTLS выдаёт свежие, в отпечатки они не входят.
  • pre_shared_key (0x0029) и early_data (0x002a) отбрасываются — свежая сессия не может возобновить захваченные PSK.
  • Key shares синтезируются из захваченных supported groups: гибридные постквантовые клиенты (X25519MLKEM768/X25519Kyber768) предлагают гибридный share плюс X25519; остальные — один share для предпочитаемой классической группы.
  • Наборы шифров, supported groups, форматы EC-точек, алгоритмы подписи, supported_versions и ALPN восстанавливаются из захвата; compress_certificate повторяется с brotli (предложение семейства Chrome); неизвестные расширения повторяются только по факту присутствия.

JA3 — это MD5 от строки legacyVersion,ciphers,extensions,groups,ecPointFormats с отфильтрованным GREASE; JA4 — форма FoxIO a_b_c.

Если по ALPN согласован h2 (и Force HTTP/1.1 выключен), транспорт uTLS сам пишет HTTP/2-префейс вместо фиксированного h2-стека Go: захваченный клиентский SETTINGS-фрейм — идентификаторы, значения и порядок — дословно, плюс захваченный порядок псевдозаголовков (HTTP/2-отпечаток в стиле Akamai).

Предпросмотр отпечатка перед отправкой

Диалог replay запрашивает отчёт «захват против replay» (POST /api/replay/fingerprint-report), который вообще не обращается к сети. Он показывает колонки Captured и Replay will send — JA3, JA4, ALPN, версию TLS, версию HTTP, User-Agent, порядок заголовков, h2 SETTINGS — и расхождения:

ИзмерениеВажностьКогда отмечается
ja4highJA4 различается — антиботы (Cloudflare, Akamai, DataDome) сравнивают его в первую очередь
ja3warningJA3 различается — его всё ещё проверяют старые наборы правил WAF
alpn / httpVersion / userAgentwarningСписки ALPN различаются; h2-захват будет отправлен как HTTP/1.1; User-Agent противоречит ClientHello
headerOrder / h2SettingswarningПорядок расходится (нативный транспорт сортирует заголовки); нативный транспорт не может воспроизвести захваченный SETTINGS-фрейм
tlsSession / headerSet / egressinfoPSK отброшен, набор заголовков отличается, отмечен выход через прокси

Загрузка mTLS-клиентских сертификатов

Откройте Settings → mTLS client certificates. Сертификаты хранятся в локальной базе SQLite; ответы API никогда не возвращают PEM.

  1. Введите Name и Pinned hosts (через запятую, например api-m.goldapple.ru).
  2. Укажите один из двух форматов:
    • Объединённый PEM — цепочка сертификатов, за которой следует приватный ключ (блок PRIVATE KEY, RSA PRIVATE KEY или EC PRIVATE KEY). Загрузки без приватного ключа отклоняются.
    • …или файл .p12/.pfx с паролем — кодируется в base64 в браузере, разблокируется на стороне сервера и перекодируется в PEM.
  3. Нажмите Store certificate. Если заданы оба варианта, приоритет у PEM. Лимит загрузки — 1 МиБ.

Привязка к хосту сопоставляется с именем целевого хоста replay: точное совпадение важнее совпадения по суффиксу, а среди суффиксных совпадений побеждает самое длинное (наиболее специфичное).

Подключение сертификата при replay

В диалоге replay установите Client certificate (mTLS) в Auto (host-pinned) (по умолчанию — бэкенд подключает сертификат, привязанный к целевому хосту, если он есть) или выберите конкретный сертификат по имени. Оба транспорта предъявляют сертификат, когда сервер присылает TLS CertificateRequest; ответ replay возвращает фактически использованный clientCertId.

[!TIP] Массовый replay ограничен 50 запросами на пакет.

Классы отказов

Каждый результат replay классифицируется, давая мутационным пробам, канареечным проверкам и массовым запускам стабильный словарь вердиктов:

КлассЗначение
(пусто) — принят2xx; это не отказ
transport-errorСбой подключения или TLS-рукопожатия — HTTP-статуса нет
client-certificate-requiredHTTP 400, тело которого совпадает с mTLS-заглушкой nginx («No required SSL certificate…»)
unauthorized / forbidden / not-found401 / 403 / 404
rate-limited429
antifraud-reject451 — антифрод-слой отклонил запрос целиком
redirect3xx — replay никогда не следует редиректам
client-error / server-errorлюбой другой 4xx / 5xx

client-certificate-required определяется сниффингом первых 4096 байт тела ответа 400 — это отличает заглушку отсутствующего mTLS-сертификата от обычного некорректного запроса. Диагноз ставится по паре: если подключение сертификата переворачивает client-certificate-required в 2xx или antifraud-reject, mTLS-барьер пройден, а следующий барьер — на прикладном уровне. Канареечные проверки оповещают о дрейфе класса отказа.

[!NOTE] Replay не следует редиректам, по умолчанию использует таймаут 30 с и исключает захваченные учётные данные, пока вы явно не включите их — см. /docs/ru/security/. Захваченные JA3/JA4 берутся из вкладки TLS-анализа (/docs/ru/analysis/comparing/).

Документация Traffic Jam. Собрано с помощью Hugo.