← Все статьи

Отпечаток HTTP/2 и как устроена строка Akamai

4 мин чтения detectionhttp2network

Ваш клиент уже отправляет ClientHello, который хешируется как Chrome, User-Agent тоже говорит Chrome, а сайт всё равно блокирует запрос до того, как отработает хоть какая-то логика страницы. Рукопожатие разобрано в статье про отпечатки TLS JA3 и JA4. Следующий слой - HTTP/2, и его первые фреймы так же зависят от библиотеки, которая их сформировала, как и рукопожатие.

Откуда взялся эталонный формат

Формат, который до сих пор выводит большинство инструментов, предложили исследователи Akamai в работе Passive Fingerprinting of HTTP/2 Clients, представленной на Black Hat Europe. HTTP/2 заставляет клиента объявить свои параметры ещё до первого запроса, протокол оставляет большую часть из них на усмотрение реализации, и каждая реализация выбирает по-своему. Пользователь на этот выбор не влияет, поэтому отпечаток указывает на программу, а не на человека. В работе рассматриваются четыре составляющие.

SETTINGS: какие параметры, в каком порядке, с какими значениями

Каждое соединение HTTP/2 начинается с фрейма SETTINGS от обеих сторон. Клиент перечисляет параметры по числовым идентификаторам: 1 размер таблицы заголовков, 2 разрешение server push, 3 максимум одновременных потоков, 4 начальный размер окна, 5 максимальный размер фрейма, 6 максимальный размер списка заголовков. Авторы обнаружили, что клиенты различаются сразу по трём признакам: какие параметры они вообще отправляют, в каком порядке и с какими значениями.

Браузер, который никогда не шлёт 3, и библиотека, которая всегда шлёт 3:100, различимы ещё до сравнения значений. Скопировать числа из дампа без их порядка бесполезно.

WINDOW_UPDATE: одно число

Сразу после SETTINGS почти каждый клиент отправляет WINDOW_UPDATE на уровне соединения и увеличивает своё окно приёма. Шаг увеличения - это размер буфера, который выбрали авторы реализации, поэтому у разных клиентов он разный, а у одного и того же постоянный.

Фреймы PRIORITY

Некоторые клиенты в начале соединения отправляют PRIORITY для потоков, которые никогда не будут открыты, и строят из них дерево зависимостей для последующих запросов. В каждом фрейме есть идентификатор потока, бит эксклюзивности, поток-родитель и вес. В примерах из работы одно семейство браузеров отправляло пять таких фреймов на каждом соединении, а другие клиенты ни одного. Текущая спецификация HTTP/2 объявила эту схему приоритетов устаревшей, поэтому на живом дампе это поле часто пустое. Пустое поле - тоже значение: если клиент выдаёт себя за браузер, который эти фреймы отправляет, а сам их не шлёт, он себе противоречит.

Порядок псевдозаголовков

В HTTP/2 строку запроса заменяют четыре псевдозаголовка: :method, :authority, :scheme, :path. Спецификация не фиксирует их порядок, поэтому каждая реализация пишет их в том порядке, который хранит её структура заголовков. В дампах из работы Chrome отправлял :method, :authority, :scheme, :path, Firefox :method, :path, :authority, :scheme, а у curl и Go был свой порядок. Порядок обычных заголовков после псевдозаголовков - отдельный сигнал, который многие детекторы тоже читают, но в строку Akamai он не входит.

Строка отпечатка

Предложенный формат склеивает четыре части через вертикальную черту:

S[;]|WU|P[,]#|PS[,]

Разобранный в работе пример, дамп Firefox, выглядит так:

1:65536;4:131072;5:16384|12517377|3:0:0:201,5:0:0:101,7:0:0:1,9:0:7:1,11:0:3:1|m,p,a,s

Эти числа относятся к браузерам того времени. Современные браузеры отправляют другие значения, так что ничего из работы не стоит зашивать в код. Снимайте строку своего клиента с живого эхо-сервиса.

Почему его не проверяют отдельно

В самой работе сказано, что энтропии отпечатка HTTP/2 недостаточно, чтобы отслеживать конкретного человека. Зато он хорошо подтверждает или опровергает заявление клиента. Приходит запрос с User-Agent: ... Chrome/.... Отпечаток TLS показывает, какая TLS-библиотека собрала ClientHello, и расширение ALPN в этом ClientHello уже объявило h2. Отпечаток HTTP/2 показывает, какая реализация HTTP/2 сформировала запрос. Если все три указывают на один и тот же браузер, запрос прошёл дешёвую проверку согласованности. Если User-Agent говорит Chrome, JA4 говорит Chrome, а строка HTTP/2 говорит net/http из Go, запрос где-то врёт, и правка заголовков до этого слоя не дотягивается.

Авторы описывают и обратное применение. Если TCP-стек клиента выглядит как Windows, а TLS и HTTP/2 как браузер на Mac, скорее всего, клиент сидит за прокси, который пересылает байты, не терминируя TLS.

Что это значит для тех, кто пишет клиентов

Этот слой принадлежит сетевому стеку, а не вашему коду запросов. Библиотека, которая имитирует браузер, должна воспроизводить все четыре части сразу и в соответствии с той версией браузера, за которую себя выдаёт. Хватит ли этого, зависит от того, что ещё проверяет сайт.

AntiBrow не переписывает слой HTTP/2. Фреймы профиля формирует собственный сетевой стек браузера, так что настраивать здесь нечего. Если ваша единственная проблема - несовпадение HTTP/2 или TLS на JSON-эндпоинте, браузер будет дорогим способом её решить, и библиотека с имитацией подойдёт лучше.

Проверьте свой клиент

Направьте клиент, который реально работает в проде, на https://tls.peet.ws/api/all. В ответе есть поле akamai_fingerprint в описанном формате рядом с отпечатками TLS. Откройте тот же адрес в обычном Chrome на той же машине и сравните строки поле за полем. Где эта проверка стоит среди других сигналов антибот-системы, описано в статье как работают антибот-системы.

Попробуйте на бесплатном тарифе.

Неограниченное число локальных профилей, без карты. Проверьте результат детекторами сами.