Назад к блогу
Отпечатки

Client Hints Fingerprinting: HTTP-заголовки и идентичность

Заголовки Client Hints, например sec-ch-ua, описывают бренд, версию и платформу в каждом запросе. Узнайте, откуда берутся расхождения и как сверять их с JavaScript.

Документация

Нужна структурированная документация по теме Отпечатки?

Эта статья относится к редакционной библиотеке. Для пошаговой настройки, справки и постоянных обновлений переходите сразу в соответствующий раздел docs.

Каждый запрос браузера на базе Chromium может нести короткое структурированное описание отправившего его браузера. Эти описания называются Client Hints, и три из них отправляются по умолчанию: Sec-CH-UA, Sec-CH-UA-Mobile и Sec-CH-UA-Platform. Они приходят в заголовках запроса раньше, чем выполнится любой скрипт страницы, поэтому сервер может прочитать бренд браузера, основную версию, класс устройства и семейство операционной системы уже при первой навигации.

Client Hints появились, чтобы заменить длинную строку User-Agent произвольного формата чем-то более узким и более управляемым. На практике они также создали второе место, где браузер описывает себя сам. Если заголовки говорят одно, navigator.userAgentData другое, а строка User-Agent третье, то само расхождение становится сигналом, который заметит проверка отпечатка. Разделы ниже описывают, что сообщает каждый слой, откуда берутся расхождения, как сравнить слои в одном сеансе и что BotBrowser документирует, а что не документирует по этой теме.

Сравнение Client Hints в одном сеансе. HTTP-заголовки, navigator.userAgentData и контексты воркеров сверяются с одной заданной идентичностью.

Что браузер отправляет до выполнения любого скрипта

Client Hints делятся на две группы. Подсказки с низкой энтропией отправляются по умолчанию. Sec-CH-UA перечисляет бренды браузера с основными версиями, Sec-CH-UA-Mobile сообщает, запрашивает ли браузер мобильный вариант, а Sec-CH-UA-Platform называет семейство операционной системы. Поскольку эти три значения похожим образом описывают значительную долю браузеров, спецификация считает их малорискованными и разрешает браузеру отправлять их без запроса.

Подсказки с высокой энтропией содержат подробности: полный список версий, версию платформы, архитектуру процессора, разрядность, модель устройства и признак того, что 32-разрядный браузер работает в 64-разрядной системе. Сервер получает их только после согласия, которое он выражает заголовком ответа Accept-CH со списком нужных подсказок, и тогда браузер включает их в последующие запросы к этому источнику. Серверу, которому подсказка нужна уже в первом запросе, доступен и Critical-CH: он просит браузер повторить запрос с приложенной подсказкой. Сторонние фреймы получают подсказку только тогда, когда страница-хозяин делегирует её заголовком Permissions-Policy.

У JavaScript есть собственный путь к тем же данным. navigator.userAgentData сразу отдаёт бренды, признак мобильности и платформу, а getHighEntropyValues() возвращает подробные значения в виде промиса, когда страница запрашивает конкретные имена. Оба пути должны описывать один и тот же браузер, поэтому их сравнение остаётся самой прямой проверкой согласованности.

Обычный запрос настольного браузера семейства Chrome несёт заголовки такой формы, как в строках ниже. Точная запись GREASE и её позиция меняются от выпуска к выпуску Chrome, поэтому значения показывают форму данных и не предназначены для копирования.

Sec-CH-UA: "Chromium";v="136", "Google Chrome";v="136", "Not.A/Brand";v="99"
Sec-CH-UA-Mobile: ?0
Sec-CH-UA-Platform: "Windows"

Заголовок User-Agent по-прежнему существует. Chrome уменьшил объём деталей в нём, и точные сведения о версии и платформе переместились в Client Hints. Поэтому у сервера есть два описания одного браузера, и он может проверить, совпадают ли они. Более подробное сравнение строки и структурированных подсказок есть в материале Custom User Agent.

В списке брендов есть и запись GREASE: намеренно странный бренд вроде Not.A/Brand, у которого текст, версия и позиция со временем меняются. Её задача в том, чтобы серверы не зашивали в код фиксированный список. Для того, кто настраивает идентичность браузера, GREASE напоминает, что список брендов строится по правилу, зависящему от версии, и что список, написанный вручную, может выглядеть неверным, даже если каждое имя бренда настоящее.

Откуда берутся расхождения

У идентичности браузера есть несколько поверхностей, и каждую читает своя часть страницы или сервера. Расхождение возникает, когда две поверхности описывают разные браузеры. Чаще всего источники такие:

  • Строка User-Agent называет одну версию браузера, а Sec-CH-UA указывает другую основную версию.
  • Платформа в Sec-CH-UA-Platform отличается от платформы в navigator.userAgentData, либо признак мобильности противоречит платформе.
  • Список брендов в заголовках отличается от брендов, которые возвращает JavaScript, например Edge в одном месте и обычный Chrome в другом.
  • Воркер сообщает иную идентичность, чем основной поток, потому что подмена применена только к страницам.
  • Подмены, внесённые двумя разными инструментами, например профиль плюс изменение User-Agent на уровне фреймворка или CDP, где каждый инструмент покрывает свою часть поверхностей.
  • Конфигурация, которая была верной в момент написания, но теперь отстаёт от выпуска Chrome, на котором собран бинарный файл браузера.

Расхождения на стороне заголовков можно прочитать на сервере без запуска скриптов страницы: заголовки по умолчанию приходят с первым запросом, а сервер, запрашивающий подсказки с высокой энтропией, получает их в следующем цикле запроса и может сопоставить с User-Agent. Для сравнений navigator.userAgentData и воркеров скрипт нужен. С точки зрения приватности Client Hints пополняют набор значений, которые вместе с другими сигналами позволяют выделить браузер. Cover Your Tracks от EFF показывает, как сочетания обычных значений становятся отличительными, а Client Hints служат ещё одним компонентом такого сочетания. Согласованный набор значений к тому же проще проверять, чем лоскутную смесь, потому что каждое найденное различие означает реальный вопрос, а не шум.

Представьте команду, которая загружает профиль настольной Windows, а затем через фреймворк тестирования задаёт строку User-Agent другой операционной системы. Фреймворк покрывает строку и, возможно, часть метаданных, профиль покрывает свои поверхности, и ни один инструмент не знает о другом. В итоге получается сеанс, в котором заголовки и значения, видимые скрипту, описывают две разные машины. Решение не в более удачной подмене. Решение в том, чтобы выбрать один источник для каждого значения и убрать второй.

Контексты выполнения важны не меньше, чем виды запросов. У выделенного воркера, общего воркера и сервис-воркера есть собственные глобальные области, а интерфейс NavigatorUAData доступен как в окнах, так и в областях воркеров. Поэтому страница может прочитать значения бренда и платформы внутри воркера и сравнить их со своими. Подмена, затрагивающая только страницу, оставляет это сравнение открытым.

Значения, связанные с платформой, требуют особого внимания, потому что зависят друг от друга. Название платформы, её версия, архитектура, разрядность и признак мобильности описывают одно устройство. Версия платформы, обычная для одной операционной системы, выглядит неверно рядом с названием другой, а признак мобильности «истина» рядом с настольной архитектурой сразу вызывает вопрос. Меняя одно из этих значений, проверяйте их все вместе.

Сравнение заголовков и значений JavaScript в одном сеансе

Цель сравнения узкая: убедиться, что один сеанс сообщает одну идентичность везде, где её можно прочитать. Для этого не нужна сторонняя страница с оценкой. Достаточно страницы и сервера, которые вы контролируете, и они позволяют намеренно запросить подсказки с высокой энтропией.

  1. Откройте тестовую страницу по HTTPS с сервера, который вы контролируете, и запишите заголовки запроса, полученные при первой навигации.
  2. Добавьте заголовок ответа Accept-CH со списком подсказок с высокой энтропией, которые вы хотите сравнить, например Sec-CH-UA-Full-Version-List, Sec-CH-UA-Platform-Version и Sec-CH-UA-Arch, и загрузите страницу второй раз, чтобы браузер их включил.
  3. На странице прочитайте navigator.userAgentData и вызовите getHighEntropyValues() с теми же именами, а результаты запишите рядом с заголовками.
  4. Повторите чтение в JavaScript из выделенного воркера и, если ваш сайт его использует, из сервис-воркера, и отметьте любое отличие от основного потока.
  5. Сравните имена брендов, основные версии, платформу, версию платформы, архитектуру, разрядность, модель и признак мобильности, добавьте строку User-Agent и запишите каждое различие вместе с запросом или контекстом, который его дал.

Публичные страницы могут дополнить такую проверку. Cover Your Tracks от EFF показывает, как ваш браузер выглядит для типичного трекера, и это служит вторым мнением о том, насколько набор отличителен. Для сравнения по заголовкам такие страницы подходят хуже, потому что не сообщают, какой запрос дал какое значение. Для этого держите собственную тестовую страницу, а публичные инструменты считайте контекстом. Более широкий порядок проверки описан в материале Verify Browser Fingerprint.

Записывайте результаты в простую таблицу: одна строка на значение и один столбец на поверхность, то есть заголовок запроса, основной поток, воркер и строка User-Agent. Строка, где столбцы различаются, это находка. Строка, где они совпадают, подтверждает только это значение и ничего не говорит о том, как конкретный сайт отнесётся к сеансу.

Найдя различие, двигайтесь от неверной поверхности назад. Выясните, какой инструмент или флаг отвечает за эту поверхность, проверьте, не пишет ли в неё ещё и второй инструмент, и уберите дублирующий источник. Затем повторите сравнение целиком, а не только упавшую строку, потому что изменение одного значения может выявить расхождение в соседнем.

Один источник Client Hints в BotBrowser

BotBrowser документирует, что настройка флагов идентичности, включая --bot-browser-brand и флаги User-Agent и платформы, порождает согласованные Client Hints: бренды, токены GREASE, значения с высокой энтропией и HTTP-заголовки Client Hints, совпадающие с navigator.userAgentData в основном потоке, воркерах и HTTP-запросах. Для описанного выше сравнения это значит, что вы можете сверить заголовки и значения JavaScript одного сеанса с единой заданной идентичностью, а не согласовывать несколько источников вручную. BotBrowser не может контролировать, как сайт классифицирует Client Hints и другие сигналы отпечатка, не может гарантировать результат обнаружения или доступа и не может согласовать подмены User-Agent на уровне CDP или фреймворка, применённые вне BotBrowser. Пользовательский User-Agent и полный контроль над userAgentData требуют документированной лицензии ENT Tier3.

Документированный путь короткий. Загрузите профиль и добавляйте флаги идентичности только тогда, когда нужен бренд или платформа, отличные от профиля. Флаги передаются в аргументах запуска, а не в параметре фреймворка. По этим входным данным BotBrowser порождает зависимые значения, поэтому список брендов и запись GREASE вам писать не нужно.

chromium-browser \
  --bot-profile="path/to/profile.enc" \
  --bot-browser-brand=edge \
  --bot-ua-full-version=142.0.7444.60 \
  --bot-brand-full-version=142.0.3595.65

Документированные значения бренда: chrome, edge, brave, opera, chromium и webview. У каждого бренда свой ритм версий, поэтому полные версии Edge и Opera отличаются от версии Chromium, даже когда основная версия общая. Используйте --bot-ua-full-version для версии Chromium и --bot-brand-full-version для версии самого бренда и держите обе на одной основной версии. Смена бренда документирована для лицензии ENT Tier2, а бренд WebView и пользовательский User-Agent документированы для ENT Tier3.

Считайте профиль и флаги идентичности единственным источником. Если вы дополнительно задаёте User-Agent через CDP или параметр фреймворка, такая подмена применяется вне BotBrowser, и BotBrowser не согласует её со сгенерированными Client Hints. Выберите один источник для каждого значения. Передавая --user-agent, используйте документированные подстановки вроде {ua-full-version}, чтобы строка следовала тем же флагам, что и подсказки.

После каждого выпуска Chrome проверяйте, что у --bot-ua-full-version та же основная версия Chromium, что и у вашего бинарного файла BotBrowser. Когда бинарный файл переходит на новую основную версию, используйте профиль и флаги, ориентированные на неё. Документированный признак расхождения: Client Hints не совпадают с бинарным файлом, а устаревшая версия сама по себе означает несогласованность между конфигурацией и браузером, который её исполняет.

Частые вопросы о Client Hints

Отправляются ли Client Hints при отключённом JavaScript?

Подсказки с низкой энтропией по умолчанию являются заголовками запроса, поэтому от скрипта страницы они не зависят. Подсказки с высокой энтропией зависят от согласия сервера через Accept-CH. Интерфейсу JavaScript, естественно, нужно выполнение скрипта. Из-за этого разделения проверка заголовков и проверка скрипта отвечают на разные вопросы, и обе входят в проверку согласованности.

Может ли сервер получить подсказку с высокой энтропией, которую он не запрашивал?

Через заголовки запроса нет. Браузер прикладывает их только после предварительного согласия. Скрипт, выполняющийся на странице, всё же может вызвать getHighEntropyValues() и отправить результат сам, и это отдельный путь со своими средствами контроля. Анализируя поведение сайта, смотрите на оба пути.

Может ли фреймворк тестирования задать Client Hints?

Фреймворки тестирования и протокол DevTools умеют подменять строку User-Agent и в некоторых случаях связанные метаданные. Что именно покрывает подмена, зависит от инструмента, а подмены от разных инструментов могут противоречить друг другу. Проверьте каждую поверхность описанным выше сравнением. BotBrowser не согласует подмены, применённые вне BotBrowser, поэтому держите значения идентичности в одном месте.

В чём разница между Sec-CH-UA и Sec-CH-UA-Full-Version-List?

Sec-CH-UA содержит бренды с основными версиями и отправляется по умолчанию. Sec-CH-UA-Full-Version-List содержит полную строку версии каждого бренда и является подсказкой с высокой энтропией, для которой нужно предварительное согласие. В JavaScript то же разделение выглядит как brands для основных версий и fullVersionList для подробных. Оба списка должны называть одни и те же бренды и одни и те же основные версии.

Определяет ли совпадение Client Hints, как сайт обойдётся с сеансом?

Нет. Client Hints являются одним из многих сигналов отпечатка. Согласованность убирает один вид расхождений, но сайты сочетают множество других входных данных, и BotBrowser не может гарантировать классификацию, результат отслеживания или результат доступа. Считайте чистое сравнение проверкой качества вашей собственной конфигурации и не более того.

Какие флаги BotBrowser влияют на Client Hints?

Документированные флаги идентичности: --user-agent, --bot-platform, --bot-platform-version, --bot-model, --bot-architecture, --bot-bitness и --bot-mobile, а также вспомогательные --bot-browser-brand, --bot-ua-full-version и --bot-brand-full-version. Флаг --user-agent поддерживает подстановки, которые берут значения из остальных флагов, поэтому строка и подсказки опираются на одни и те же значения.

Контрольный список перед тем, как положиться на конфигурацию

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

  • У каждого значения один источник: профиль, флаг идентичности или ничего другого.
  • Бренды, платформа и признак мобильности совпадают в заголовках запроса, основном потоке и воркерах.
  • Значения с высокой энтропией, запрошенные через Accept-CH, совпадают с результатами getHighEntropyValues().
  • Строка User-Agent и Client Hints называют одну и ту же основную версию Chromium.
  • Основная версия в --bot-ua-full-version совпадает с бинарным файлом BotBrowser после каждого выпуска.
  • Уровень лицензии покрывает используемые флаги.

Для дальнейшего чтения: в материале Navigator Properties Fingerprinting разобраны видимые скрипту значения navigator, соседствующие с navigator.userAgentData, а What Is Browser Fingerprinting даёт более широкий контекст о том, как сигналы объединяются.

Публичные источники

#client hints#Sec-Ch-Ua#fingerprinting#GREASE#user agent#приватность#идентичность браузера#HTTP-заголовки

Переведите BotBrowser из исследований в продакшн

Используйте эти руководства, чтобы понять модель, а затем перейти к кроссплатформенной валидации, изолированным контекстам и масштабируемому браузерному развертыванию.