Как JA3 и JA4 ловят скрапер ещё до загрузки страницы
Скрипт отправляет запрос с заголовками, заявляющими свежий Chrome на Windows. Сайт возвращает блокировку меньше чем за 50 миллисекунд, до того как сформировано тело ответа и до того как выполнится хоть какой-то JavaScript. Заголовки никто не читал. Первым выдало рукопожатие TLS.
Client hello несёт подпись реализации
До того как пойдут прикладные данные, TLS-клиент отправляет ClientHello: версию протокола, список наборов шифров, список расширений, поддерживаемые эллиптические кривые и алгоритмы подписи, каждое в определённом порядке. Этот порядок не настраивается тем, кто делает запрос. Он зашит в ту TLS-библиотеку, которая собрала пакет. Chrome использует BoringSSL. Модуль ssl в Python и requests стоят поверх OpenSSL с другим порядком. crypto/tls в Go даёт третий вариант. curl слинкован с той библиотекой, с которой собрали сборку. Два клиента могут заявлять совершенно одинаковый User-Agent и при этом слать рукопожатия, которых настоящий Chrome не отправил бы никогда.
JA3 и JA4 хешируют ровно этот порядок
JA3 склеивает версию TLS, список наборов шифров, список расширений, эллиптические кривые и форматы точек эллиптических кривых в одну строку и берёт от неё MD5. Конкретная версия TLS-библиотеки, использованная обычным образом, даёт один и тот же хеш JA3 каждый раз, что бы ни ушло в заголовках позже. JA4 - более новый формат от FoxIO, сделанный так, чтобы хуже поддаваться тривиальной рандомизации и лучше читаться глазами (он кодирует количества и усечённый хеш шифров и расширений раздельно, а не одним непрозрачным MD5). Оба существуют потому, что рукопожатие - куда более надёжный признак, чем заголовок: поддельное рукопожатие нельзя отправить, не реализовав другой TLS-стек по-настоящему.
Почему скопированный заголовок не переживает столкновения
Коммерческий антибот-вендор проверяет это до того, как запрос дойдёт до прикладного кода. Заявлено - Chrome. Хеш JA3 или JA4 говорит urllib3 из Python, или Go, или голый OpenSSL-клиент. Это противоречие видно на сетевой границе, до маршрутизации и до всякой логики страницы, поэтому блокировки на этом слое мгновенны и поэтому более убедительные заголовки никогда их не чинят. Несовпадение находится на слой ниже того, который вы правите.
HTTP/2 добавляет вторую независимую подпись
За рукопожатием у HTTP/2 есть собственный отпечаток, который иногда обсуждают отдельно как отпечаток в стиле Akamai. В него входят значения фрейма SETTINGS, которые клиент шлёт первыми (размер таблицы заголовков, начальный размер окна, максимум одновременных потоков), порядок отправки этих настроек, порядок псевдозаголовков в запросе (:method, :authority, :scheme, :path - у Chrome он не такой, как у curl или nghttp2), порядок обычных заголовков внутри фрейма и наличие фреймов PRIORITY у потока. Как и рукопожатие TLS, всё это решает библиотека, а не то, что автор скрипта пишет в словарь заголовков.
Почему это довод в пользу настоящего браузера
Ни один из этих слоёв нельзя залатать добавлением заголовков к запросу. Рукопожатие TLS и раскладка фреймов HTTP/2 порождаются самим сетевым стеком, ниже кода, которым управляет автор скрапера. Это одна из недооценённых причин, почему настоящий браузер обыгрывает самодельный HTTP-клиент ещё до выполнения первой строки JavaScript: реализация BoringSSL и HTTP/2 в браузере даёт правильную подпись по построению, потому что она и есть правильная реализация, а не её приближение.
Где здесь AntiBrow, а где нет
AntiBrow не модифицирует TLS-стек. В движке браузера нет кода, который переставлял бы наборы шифров или подделывал значение JA3 или JA4. Польза уже и честнее: каждый запущенный профиль работает на немодифицированном сетевом стеке Chromium, поэтому его рукопожатие и подпись HTTP/2 - те же, что у настоящей установки Chrome, потому что это и есть настоящая установка Chrome. Этот слой продукт не проектирует. Он побочный эффект автоматизации браузера вместо HTTP-клиента.
Когда обычный HTTP-клиент - выбор лучше
Если эндпоинт, к которому вы обращаетесь, не снимает отпечатки TLS и HTTP/2, а так устроено большинство внутренних API и большинство сайтов без коммерческого антибота впереди, обычный вызов requests или httpx быстрее, дешевле и занимает долю памяти полноценного браузерного процесса. Гонять движок браузера против цели, которая никогда не смотрит на рукопожатие, - трата ресурсов впустую. Для промежуточного случая открытые инструменты вроде curl_cffi воспроизводят JA3/JA4 и порядок HTTP/2 конкретного браузера без стоимости движка отрисовки, и это верный выбор, когда вы уже убедились, что блокирует именно этот слой, а выполнение JavaScript и постоянное состояние профиля вам не нужны. Полноценный браузер берите, когда вам нужны ещё и выполнение JS, согласованность canvas и WebGL или профиль, живущий между сессиями.
Проверяйте, а не предполагайте
Посмотрите подпись собственного клиента, прежде чем гадать, что дело в этом слое. tls.peet.ws и ja3er.com покажут хеш JA3/JA4 и подпись HTTP/2 того, что отправило запрос, скрипта или вкладки браузера, и позволят сравнить с известным хешем Chrome, а не предполагать.
Где этот слой находится внутри остального решения антибота - в статье как работают антибот-системы. Про слой выше, начинающийся с выполнения JavaScript, - в статье что такое отпечаток браузера.
Попробуйте на бесплатном тарифе.
Неограниченное число локальных профилей, без карты. Проверьте результат детекторами сами.