Профили отпечатков и канарейки

Снимки версионируемых профилей отпечатков и оповещения о дрейфе классов отказа.

Когда целевое приложение выпускает обновление, его клиентская идентичность меняется — новый User-Agent, подросший заголовок версии SDK, перекомпилированный TLS ClientHello. Коллектор, собранный под старую форму, продолжает отправлять старую идентичность и начинает ловить срабатывания антифрод-гейта. Профили отпечатков и проверки-канарейки превращают эту тихую поломку в громкое оповещение: профили снимают слепок идентичности, которую демонстрирует захват (с версионированием), а канарейки периодически проигрывают закреплённый эндпоинт и сигнализируют о любом отклонении от базового класса отказа.

Обе функции живут на вкладке API lab → Collector, в представлении Profiles. Всё хранится в SQLite-базе воркспейса (тот же файл, что и захваты — по умолчанию .traffic-jam/traffic_jam.sqlite3, путь можно переопределить переменной TRAFFIC_JAM_DB_PATH), так что профили и канарейки переживают перезапуски API.

[!NOTE] Канарейки проигрывают реальные запросы против живого эндпоинта. Закрепляйте только эндпоинты систем, которые вам разрешено тестировать, и помните про 10-минутный интервал, если цель чувствительна к rate limit.

Снимок профиля отпечатка

Профиль — это версионируемый снимок клиентской идентичности одного захваченного запроса. Бэкенд извлекает из исходного запроса следующие маркеры:

ПолеИсточник
headersВсе заголовки запроса, кроме псевдозаголовков HTTP/2 (:method, :path, …), в порядке захвата
userAgentЗаголовок User-Agent
appVersionЗаголовок plaid-version, если присутствует
eventUrlParamsQuery-параметры as и p, если присутствуют
ja3, ja4, alpn, tlsVersionПоля отпечатка TLS ClientHello запроса
capturedAtМетка времени снимка (UTC, RFC 3339)

Чтобы создать снимок:

  1. Откройте API lab → Collector → Profiles.
  2. В разделе Profiles выберите определяющий запрос в пикере Profile source (сначала сессия, затем запрос).
  3. Введите Profile name — если оставить пустым, по умолчанию возьмётся host (authority) запроса.
  4. Нажмите Snapshot profile version.

Версионирование идёт по имени: повторный снимок под существующим именем сохраняет version = max(existing) + 1, а не перезаписывает, поэтому список профилей хранит историю того, как эволюционировала идентичность. Каждая строка показывает имя, бейдж vN, захваченную версию приложения (если есть) и дату снимка; иконка корзины удаляет версию.

Закрепление канарейки

Канарейка закрепляет один эндпоинт — пару «захваченная сессия/запрос» — плюс опции replay, воспроизводящие базовый результат, и записывает, как выглядит «норма»: в виде статус-кода и класса отказа.

  1. В том же представлении Profiles, в разделе Canaries, выберите эндпоинт в пикере Canary endpoint.
  2. Введите Canary name (например, product card).
  3. Опционально прикрепите Profile из дропдауна (name vN), чтобы привязать канарейку к конкретной версии идентичности.
  4. Нажмите Pin canary.

Базовый результат берётся из захваченного ответа: его статус-код, а базовый класс отказа устанавливается в accepted. Подтверждающий тост предлагает сразу прогнать канарейку один раз, чтобы убедиться, что отпечаток всё ещё проходит. Сохранённые опции replay зеркалят ручки интерактивного транспорта replay:

{
  "transport": "utls",
  "fingerprintProfile": "<profile id>",
  "forceHttp1": false,
  "clientCertId": "<client cert id>",
  "headerOverrides": { "X-Example": "value" },
  "body": "optional replacement body"
}

Прогон канарейки выполняет replay с включёнными захваченными чувствительными значениями и заранее принятыми подтверждениями мутаций/назначения, так что запрос воспроизводится точно таким, каким был базовый, — без интерактивных подсказок.

Прогон канарейки и чтение вердикта

Каждая строка канарейки показывает её базовый результат (baseline 200 accepted) и последний прогон в виде бейджа: last run 200 · stable, last run 451 · drift или never run. Нажмите Run now, чтобы выполнить одну проверку по требованию.

Прогон проигрывает закреплённый запрос, классифицирует ответ в класс отказа и сравнивает его с базовым. Дрейф — это любое изменение класса отказа, в любую сторону:

  • acceptedantifraud-reject: отпечаток устарел; гейт теперь отклоняет то, что раньше принимал. Это и есть тревожный случай.
  • antifraud-rejectaccepted: ранее отклоняемый запрос теперь проходит — гейт изменился; перевыставьте базовый результат.
  • Без изменений: вердикт stable.

Прогон с дрейфом возвращает вердикт вида drift: accepted → anti-fraud rejection (451), а UI поднимает предупреждающий тост с именем канарейки. Каждый прогон записывается (метка времени, статус, класс отказа, флаг дрейфа, ошибка транспорта, если была); список прогонов хранит самые свежие первыми, с потолком 50 на выборку (слой хранения принимает limit до 200). Удаление канарейки каскадно удаляет её историю прогонов.

Словарь классов отказа — тот же, что используют replay, мутационные пробы и conformance diff:

КлассЗначение
(пусто)accepted2xx, не отказ
transport-errorСбой соединения/TLS до получения ответа
client-certificate-required400, чьё тело называет недостающий mTLS-сертификат клиента
unauthorized / forbidden / not-found401 / 403 / 404
rate-limited429
antifraud-reject451 — антифрод-гейт
redirect3xx (replay никогда не следует редиректам)
client-error / server-errorпрочие 4xx / 5xx

Запланированные оповещения о дрейфе

Пока приложение открыто, представление Profiles повторно прогоняет каждую закреплённую канарейку с фиксированным интервалом в 10 минут и обновляет историю прогонов. Дрейф, обнаруженный в запланированном прогоне, показывает тот же предупреждающий тост, что и ручной прогон, так что коллектор, сломавшийся между прогонами пайплайна, ловится именно здесь — на уровне отпечатка, — а не позже, в виде необъяснимых сбоев пайплайна данных. Таймер работает только пока воркспейс открыт; фонового демона нет.

[!TIP] Дрейф версии приложения — обычный убийца коллектора. Когда канарейка дрейфует, снимите свежую версию профиля с нового захвата обновлённого приложения, сделайте diff заголовков и JA3/JA4 двух версий профиля и перенесите дельту в свой коллектор. Затем diff /docs/ru/collector/conformance/ может подтвердить, что ваш обновлённый парсер совпадает с новым эталонным захватом.

Где это вписывается

Профили питают транспорт replay — опция канарейки fingerprintProfile выбирает, какую снятую идентичность предъявляет replay (см. /docs/ru/replay/fingerprints/ про управление TLS-отпечатком). Когда канарейка срабатывает, цикл такой: перезахватить обновлённое приложение (/docs/ru/capture/importing/), снять новую версию профиля, перезакрепить или перевыставить базовый результат канарейки и убедиться, что экспортированный коллектор всё ещё соответствует (/docs/ru/collector/conformance/).

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