Переход на клиентские подсказки User-Agent

Стратегии перехода вашего сайта от использования строки пользовательского агента к новым клиентским подсказкам пользовательского агента.

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

В этой статье вы узнаете, как проверить доступ к данным пользовательского агента и перенести использование строки пользовательского агента в клиентские подсказки пользовательского агента.

Аудит сбора и использования данных пользовательского агента

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

Если вы не знаете, используются ли или где данные пользовательского агента, рассмотрите возможность поиска в интерфейсном коде использования navigator.userAgent и в внутреннем коде для использования HTTP-заголовка User-Agent . Вам также следует проверить свой внешний код на предмет использования уже устаревших функций, таких как navigator.platform и navigator.appVersion .

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

  • Название или версия браузера
  • Название или версия операционной системы
  • Марка или модель устройства
  • Тип процессора, архитектура или разрядность (например, 64-разрядный)

Также вероятно, что вы используете стороннюю библиотеку или службу для обработки пользовательского агента. В этом случае проверьте, обновляются ли они для поддержки подсказок клиента User-Agent.

Вы используете только основные данные пользовательского агента?

Набор клиентских подсказок User-Agent по умолчанию включает в себя:

  • Sec-CH-UA : имя браузера и основная/значительная версия.
  • Sec-CH-UA-Mobile : логическое значение, указывающее мобильное устройство.
  • Sec-CH-UA-Platform : имя операционной системы.
    • Обратите внимание, что это было обновлено в спецификации и вскоре будет отражено в Chrome и других браузерах на базе Chromium.

Предлагаемая сокращенная версия строки пользовательского агента также сохранит эту базовую информацию с обратной совместимостью. Например, вместо Chrome/90.0.4430.85 строка будет включать Chrome/90.0.0.0 .

Если вы проверяете строку пользовательского агента только на предмет имени браузера, основной версии или операционной системы, ваш код продолжит работать, хотя вы, вероятно, увидите предупреждения об устаревании.

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

Стратегия: API JavaScript на стороне клиента по требованию.

Если вы в настоящее время используете navigator.userAgent вам следует перейти к предпочтению navigator.userAgentData прежде чем вернуться к анализу строки пользовательского агента.

if (navigator.userAgentData) {
  // use new hints
} else {
  // fall back to user-agent string parsing
}

Если вы проверяете мобильный или настольный компьютер, используйте логическое значение mobile :

const isMobile = navigator.userAgentData.mobile;

userAgentData.brands — это массив объектов со свойствами brand и version , в котором браузер может указать свою совместимость с этими брендами. Вы можете получить к нему доступ напрямую как к массиву или можете использовать вызов some() чтобы проверить наличие определенной записи:

function isCompatible(item) {
  // In real life you most likely have more complex rules here
  return ['Chromium', 'Google Chrome', 'NewBrowser'].includes(item.brand);
}
if (navigator.userAgentData.brands.some(isCompatible)) {
  // browser reports as compatible
}

Если вам нужно одно из более подробных значений пользовательского агента с высокой энтропией, вам нужно будет указать его и проверить результат в возвращенном Promise :

navigator.userAgentData.getHighEntropyValues(['model'])
  .then(ua => {
    // requested hints available as attributes
    const model = ua.model
  });

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

Стратегия: статический заголовок на стороне сервера.

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

Рассмотрите эту стратегию, если вы преобразуете или настраиваете ответы, предоставляемые на основе данных пользовательского агента.

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

Например, текущие значения по умолчанию для Chrome будут представлены как:

⬇️ Заголовки ответов

Accept-CH: Sec-CH-UA-Mobile, Sec-CH-UA-Platform, Sec-CH-UA

Если бы вы также хотели получать в ответах модель устройства, то вы бы отправили:

⬇️ Заголовки ответов

Accept-CH: Sec-CH-UA-Mobile, Sec-CH-UA-Model, Sec-CH-UA-Platform, Sec-CH-UA

При обработке этого на стороне сервера вам следует сначала проверить, был ли отправлен желаемый заголовок Sec-CH-UA , а затем вернуться к анализу заголовка User-Agent если он недоступен.

Стратегия: делегирование подсказок для запросов из разных источников.

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

Например, предположим, что https://blog.site размещает ресурсы на https://cdn.site , которые могут возвращать ресурсы, оптимизированные для конкретного устройства. https://blog.site может запросить подсказку Sec-CH-UA-Model , но должен явно делегировать ее https://cdn.site используя заголовок Permissions-Policy . Список подсказок, управляемых политикой, доступен в проекте инфраструктуры подсказок Clients Hints.

⬇️ Ответ сайта blog.site с делегированием подсказки

Accept-CH: Sec-CH-UA-Model
Permissions-Policy: ch-ua-model=(self "https://cdn.site")

⬆️ Запрос на подресурсы на cdn.site включающий делегированную подсказку.

Sec-CH-UA-Model: "Pixel 5"

Вы можете указать несколько подсказок для нескольких источников, а не только из диапазона ch-ua :

⬇️ Ответ сайта blog.site делегирующий несколько подсказок нескольким источникам.

Accept-CH: Sec-CH-UA-Model, DPR
Permissions-Policy: ch-ua-model=(self "https://cdn.site"),
                    ch-dpr=(self "https://cdn.site" "https://img.site")

Стратегия: делегирование подсказок iframe

iframe из разных источников работают аналогично ресурсам из разных источников, но вы указываете подсказки, которые хотите делегировать, в allow .

⬇️ Ответ с blog.site

Accept-CH: Sec-CH-UA-Model

↪️ HTML для blog.site

<iframe src="https://widget.site" allow="ch-ua-model"></iframe>

⬆️ Запрос на widget.site

Sec-CH-UA-Model: "Pixel 5"

allow в iframe переопределит любой заголовок Accept-CH , который widget.site может отправить сам, поэтому убедитесь, что вы указали все, что понадобится сайту iframe.

Стратегия: динамические подсказки на стороне сервера.

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

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

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

⬇️ Заголовки ответов для /blog

Accept-CH: Sec-CH-UA-Mobile, Sec-CH-UA-Platform, Sec-CH-UA

⬇️ Заголовки ответов для /app

Accept-CH: Sec-CH-UA-Mobile, Sec-CH-UA-Platform, Sec-CH-UA-Platform-Version, Sec-CH-UA

Стратегия: подсказки на стороне сервера требуются по первому запросу.

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

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

Для дополнительных подсказок по первому запросу есть два варианта. Во-первых, вы можете использовать заголовок Critical-CH . Он имеет тот же формат, что и Accept-CH , но сообщает браузеру, что он должен немедленно повторить запрос, если первый был отправлен без критической подсказки.

⬆️ Первоначальный запрос

[With default headers]

⬇️ Заголовки ответов

Accept-CH: Sec-CH-UA-Model
Critical-CH: Sec-CH-UA-Model

🔃 Браузер повторяет первоначальный запрос с дополнительным заголовком.

[With default headers + …]
Sec-CH-UA-Model: Pixel 5

Это повлечет за собой накладные расходы на повторную попытку самого первого запроса, но стоимость реализации относительно невелика. Отправьте дополнительный заголовок, а браузер сделает все остальное.

В ситуациях, когда вам действительно требуются дополнительные подсказки при самой первой загрузке страницы, предложение Client Hints Reliability прокладывает путь для указания подсказок в настройках уровня соединения. При этом используется расширение настроек протокола уровня приложений (ALPS) для TLS 1.3, чтобы обеспечить раннюю передачу подсказок при соединениях HTTP/2 и HTTP/3. Это все еще находится на очень ранней стадии, но если вы активно управляете собственными настройками TLS и соединения, то это идеальное время, чтобы внести свой вклад.

Стратегия: поддержка устаревших версий

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

Retrofill UA-CH — это небольшая библиотека, которая позволяет перезаписывать navigator.userAgent новой строкой, созданной на основе запрошенных значений navigator.userAgentData .

Например, этот код сгенерирует строку пользовательского агента, которая дополнительно включает подсказку «модель»:

import { overrideUserAgentUsingClientHints } from './uach-retrofill.js';
overrideUserAgentUsingClientHints(['model'])
  .then(() => { console.log(navigator.userAgent); });

Результирующая строка будет отображать модель Pixel 5 , но по-прежнему будет отображать уменьшенную версию 92.0.0.0 поскольку подсказка uaFullVersion не запрашивалась:

Mozilla/5.0 (Linux; Android 10.0; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.0.0 Mobile Safari/537.36

Дальнейшая поддержка

Если эти стратегии не охватывают ваш вариант использования, начните обсуждение в репозитории Privacy-Sandbox-dev-support , и мы сможем вместе изучить вашу проблему.

Фото Рикардо Роча на Unsplash