Как работает HTTP/2-фингерпринтинг: фреймы SETTINGS, порядок псевдозаголовков и как отпечаток Akamai дополняет JA3/JA4 при обнаружении ботов.
Каждое HTTPS-соединение начинается с TCP-рукопожатия и TLS-согласования, однако HTTP/2-фингерпринтинг работает не на этих этапах. Он извлекает сигнатуры из фреймов, которые клиент отправляет после установки TLS — в первые миллисекунды HTTP/2-сессии, до того как отправлен первый запрос. Точная комбинация фреймов настроек, прироста окна управления потоком и порядка псевдозаголовков определяется HTTP/2-реализацией клиента, а не пользователем, и остаётся одинаковой от запроса к запросу. Именно это постоянство и делает её отпечатком.
Ключевые выводы
- HTTP/2-клиенты раскрывают себя ещё до первого запроса. Начальный фрейм SETTINGS, прирост WINDOW_UPDATE и фреймы PRIORITY диктуются HTTP/2-библиотекой, а не пользователем, что создаёт различимые сигнатуры для Chrome, Firefox, Safari и инструментов автоматизации.
- Отпечаток Akamai кодирует четыре сигнала — значения SETTINGS, прирост окна, фреймы PRIORITY и порядок псевдозаголовков — в компактную строку, идентифицирующую HTTP-клиент.
- Он ловит ботов, прошедших TLS и проверки User-Agent. Скрипт, подделывающий TLS-рукопожатие Chrome с помощью
curl-impersonate, как правило, провалит проверку на уровне HTTP/2, если не воспроизведёт также точную последовательность фреймов и порядок псевдозаголовков Chrome. - В паре с JA3/JA4 два отпечатка охватывают дополняющие друг друга уровни: TLS идентифицирует криптографическую библиотеку; HTTP/2-фингерпринтинг — HTTP-реализацию. Несоответствие между ними — сильный сигнал о боте.
- Фреймы PRIORITY — исторически наиболее характерная часть HTTP/2-отпечатка — упразднены в RFC 9113, поэтому современный фингерпринтинг в большей мере опирается на значения SETTINGS и порядок псевдозаголовков.
Что такое HTTP/2 и почему он имеет отпечаток?
HTTP/2 (RFC 7540, обновлённый RFC 9113) заменил модель HTTP/1.1 с открытым текстом и обработкой одного запроса за раз на двоичный уровень кадрирования. Всё взаимодействие происходит по одному TCP-соединению через нумерованные потоки с помощью двоичных фреймов — DATA, HEADERS, SETTINGS, WINDOW_UPDATE, PRIORITY и других.
Открывая HTTP/2-соединение, клиент следует предписанной последовательности запуска прежде, чем сможет отправить первый запрос:
- Отправить предисловие соединения HTTP/2 — фиксированную 24-байтовую магическую строку.
- Отправить фрейм SETTINGS, объявляя параметры клиента для данного соединения.
- Опционально отправить фрейм WINDOW_UPDATE для увеличения начального окна управления потоком.
- Опционально отправить фреймы PRIORITY для построения дерева зависимостей потоков.
- Отправить первый фрейм HEADERS с фактическим HTTP-запросом.
Шаги 2–4 происходят до того, как сервер ответил, и до запуска любого пользовательского кода. Они полностью определяются HTTP/2-библиотекой — и различаются между реализациями стабильно, не меняясь от сессии к сессии, от URL к URL, от IP к IP.
Четыре сигнала отпечатка Akamai
Исследователи Akamai Technologies обнаружили, что сигналы запуска достаточно стабильны для каждого клиента, чтобы служить отпечатком. Отпечаток Akamai кодирует четыре сигнала в одну строку, разделённую вертикальными чертами. Типичный отпечаток выглядит так:
1:65536;3:1000;4:6291456;6:262144|15663105|0|m,a,s,p
1. Значения фрейма SETTINGS
Фрейм SETTINGS представляет собой список пар id_параметра:значение. HTTP/2 определяет шесть стандартных параметров: HEADER_TABLE_SIZE (ID 1), ENABLE_PUSH (2), MAX_CONCURRENT_STREAMS (3), INITIAL_WINDOW_SIZE (4), MAX_FRAME_SIZE (5) и MAX_HEADER_LIST_SIZE (6). Клиент отправляет только те параметры, которые хочет переопределить относительно значений протокола по умолчанию.
Комбинация того, какие параметры присутствуют, их значений и порядка следования определяется HTTP/2-библиотекой. HTTP-стек Chrome на базе BoringSSL выбирает иные значения и порядок, чем Necko в Firefox, CFNetwork в Safari или httpx в Python. Даже незначительные обновления версии могут изменить набор передаваемых параметров или их значения. Первый сегмент строки отпечатка Akamai — 1:65536;3:1000;4:6291456;6:262144 из приведённого выше примера — кодирует эти пары.
2. Прирост WINDOW_UPDATE
Система управления потоком HTTP/2 начинает с окна соединения по умолчанию в 65 535 байт. Большинство клиентов немедленно отправляет фрейм WINDOW_UPDATE, увеличивая его до более рабочего размера, и конкретная величина прироста характерна для данного клиента. Отсутствие WINDOW_UPDATE или необычное значение прироста само по себе является сигналом — многие минимальные HTTP-библиотеки полностью опускают этот фрейм, тогда как крупные браузеры отправляют большой, специфичный для своей библиотеки прирост. Второй сегмент отпечатка кодирует это значение.
3. Последовательность фреймов PRIORITY
HTTP/2 изначально позволял клиентам объявлять приоритет потоков с помощью фреймов PRIORITY, строя дерево зависимостей, чтобы сервер знал, какие ответы отправлять первыми. Браузеры отправляли характерные стартовые паттерны: исторический паттерн Chrome, например, включал специфическую последовательность фреймов PRIORITY для потоков 3, 5, 7, 1 и 11 с заданными весами. Эти паттерны были крайне стабильны и сразу отличались от HTTP-клиентов вне браузеров, которые обычно вообще не отправляют фреймы PRIORITY.
RFC 9113 (2022) упразднил механизм приоритетов потоков HTTP/2 — он оказался сложным в реализации и редко улучшал задержки на практике. Современные клиенты всё чаще опускают фреймы PRIORITY. Третий сегмент отпечатка Akamai фиксирует этот паттерн (или его отсутствие), позволяя по-прежнему отличать более старые версии браузеров от безголовых или скриптовых HTTP-клиентов.
4. Порядок псевдозаголовков
HTTP/2-запросы кодируют метод, путь, схему и хост как псевдозаголовки — :method, :path, :scheme и :authority — которые должны предшествовать обычным заголовкам во фрейме HEADERS. Протокол требует, чтобы все четыре псевдозаголовка шли перед обычными заголовками, но порядок между ними самими остаётся на усмотрение реализации.
Браузеры всегда используют определённый порядок. Chrome обычно отправляет :method, :authority, :scheme, :path — в отпечатке это кодируется как m,a,s,p. HTTP-библиотеки в Go, Python и Node.js нередко выдают псевдозаголовки в другой последовательности или меняют её от версии к версии. Четвёртый сегмент фиксирует этот порядок, и в сочетании с сигналом SETTINGS он является одним из наиболее стабильных маркеров, отличающих настоящий браузерный клиент от библиотеки автоматизации.
Как HTTP/2-фингерпринтинг дополняет TLS-фингерпринтинг
TLS-фингерпринтинг (JA3/JA4) извлекает сигнатуру из сообщения ClientHello — отправленного открытым текстом до установки шифрования TLS. HTTP/2-фингерпринтинг извлекает сигнатуру из бинарных фреймов, отправляемых сразу после установки TLS. Они работают на соседних уровнях без пересечений:
| Уровень | Техника | Что фиксируется |
|---|---|---|
| TLS | JA3 / JA4 | Наборы шифров, расширения, эллиптические кривые в ClientHello |
| HTTP/2 | Отпечаток Akamai | Значения SETTINGS, прирост окна, последовательность PRIORITY, порядок псевдозаголовков |
Бот, успешно клонировавший TLS-рукопожатие Chrome с помощью curl-impersonate, всё равно отправляет HTTP/2-фреймы из своей библиотеки — с другим набором SETTINGS и иным порядком псевдозаголовков, который любой сервер с поддержкой фингерпринтинга легко отличит от настоящего браузера. Использование обоих отпечатков вместе существенно труднее обойти, чем каждого по отдельности.
Это тот же принцип составных слоёв, что описан в руководстве по методам обнаружения ботов: несоответствие между наблюдаемыми уровнями протокола — более весомое доказательство подмены, чем единичная аномалия.
Порядок HTTP-заголовков: смежный сигнал
Помимо бинарных сигналов кадрирования, порядок обычных HTTP-заголовков во фрейме HEADERS также несёт информацию. Chrome, Firefox и Safari выдают заголовки — Accept, Accept-Language, Accept-Encoding, User-Agent, Upgrade-Insecure-Requests — в фиксированном порядке, определяемом логикой подготовки запросов браузера, а не страницей или пользователем. Библиотеки автоматизации выдают заголовки в другом порядке по умолчанию — нередко алфавитном или в порядке добавления в коде.
Запрос, порядок заголовков которого соответствует requests Python или net/http Go, а не стандартному порядку Chrome, — скорее всего, не настоящий браузер, что бы ни сообщал заголовок User-Agent. Фингерпринтинг по порядку заголовков работает как на HTTP/1.1, так и на HTTP/2, и обычно применяется совместно с бинарными сигналами кадрирования для более богатой совокупной оценки.
Практическое тестирование
В отличие от сигналов браузерного фингерпринтинга — таких как Canvas или WebGL, — которые доступны JavaScript прямо на странице, HTTP/2-фингерпринтинг по своей природе является серверным. Фреймы SETTINGS и WINDOW_UPDATE — это бинарное кадрирование, которое никогда не достигает контекста JavaScript на странице, поэтому для тестирования нужен сервер, который читает и интерпретирует эти сырые фреймы до передачи соединения на прикладной уровень.
Эндпоинт /http2 на BrowserLeaks показывает ваш актуальный отпечаток Akamai — четырёхсегментную строку, которую ваш текущий клиент отправляет прямо сейчас. Инструмент обнаружения ботов BrowserInsight охватывает дополняющие клиентские сигналы (артефакты автоматизации, утечки безголового браузера, консистентность отпечатка). TLS-уровень представлен отдельно на странице диагностики сети.
Часто задаваемые вопросы
Есть ли у HTTP/3 аналогичный отпечаток?
Да, хотя сигналы различаются. HTTP/3 работает поверх QUIC, а не TCP+TLS, и QUIC несёт собственные параметры соединения — транспортные параметры в пакете QUIC Initial, — которые различаются в зависимости от реализации. Фреймы SETTINGS HTTP/3, передаваемые в управляющем потоке QPACK, обеспечивают сигнал, аналогичный SETTINGS HTTP/2. По мере распространения HTTP/3 активно ведётся работа по распространению HTTP/2-стиля фингерпринтинга на HTTP/3.
Может ли бот обойти HTTP/2-фингерпринтинг, подделав User-Agent?
Нет. Заголовок User-Agent задаётся кодом приложения во фрейме HEADERS, но фреймы SETTINGS, WINDOW_UPDATE и PRIORITY отправляются HTTP/2-библиотекой до запуска любого кода приложения. Бот, подделывающий User-Agent Chrome, но использующий net/http Go или httpx Python, всё равно отправляет значения SETTINGS и порядок псевдозаголовков этой библиотеки, а не Chrome.
Применяется ли HTTP/2-фингерпринтинг в продакшне сегодня?
Да. Крупные CDN, вендоры по управлению ботами и большие веб-сервисы используют его совместно с TLS-фингерпринтингом и поведенческими сигналами в рамках многоуровневой оценки ботов. Поскольку он не требует выполнения JavaScript и перехватывает фреймы до отправки сервером какого-либо ответа, его особенно сложно обойти без точной эмуляции на уровне библиотеки.
Раз PRIORITY так ценен для фингерпринтинга, зачем его упразднили?
PRIORITY был удалён в RFC 9113 по причинам, никак не связанным с фингерпринтингом, — реализаторы нашли его сложным в правильной поддержке, а измеримого улучшения задержек на практике он давал крайне мало. Упразднение уменьшает одно измерение отпечатка для современных клиентов, однако это также означает: любой клиент, по-прежнему отправляющий старый паттерн PRIORITY, скорее всего использует более старую версию браузера — что само по себе является полезным классификационным сигналом.


