Migrer vers les indicateurs client user-agent

Stratégies permettant de passer de la chaîne user-agent aux nouveaux indicateurs client user-agent.

La chaîne User-Agent est une surface d'empreinte digitale passive importante dans les navigateurs, et elle est difficile à traiter. Toutefois, il existe toutes sortes de raisons valables de collecter et de traiter les données de l'agent utilisateur. Il est donc nécessaire de trouver une meilleure solution. Les indices client User-Agent fournissent un moyen explicite de déclarer votre besoin de données d'agent utilisateur et des méthodes pour renvoyer les données dans un format facile à utiliser.

Cet article vous explique comment auditer votre accès aux données de l'user-agent et migrer l'utilisation de la chaîne user-agent vers les hints client user-agent.

Auditer la collecte et l'utilisation des données de l'User-Agent

Comme pour toute forme de collecte de données, vous devez toujours comprendre pourquoi vous les collectez. La première étape, que vous preniez ou non des mesures, consiste à comprendre où et pourquoi vous utilisez les données de l'user-agent.

Si vous ne savez pas si les données de l'user-agent sont utilisées ni où, envisagez de rechercher dans votre code front-end l'utilisation de navigator.userAgent et dans votre code back-end l'utilisation de l'en-tête HTTP User-Agent. Vous devez également vérifier que votre code front-end n'utilise pas de fonctionnalités déjà obsolètes, telles que navigator.platform et navigator.appVersion.

D'un point de vue fonctionnel, réfléchissez à tous les endroits de votre code où vous enregistrez ou traitez:

  • Nom ou version du navigateur
  • Nom ou version du système d'exploitation
  • Marque ou modèle de l'appareil
  • Type, architecture ou nombre de bits du processeur (par exemple, 64 bits)

Il est également probable que vous utilisiez une bibliothèque ou un service tiers pour traiter l'user-agent. Dans ce cas, vérifiez s'ils sont mis à jour pour prendre en charge les hints client user-agent.

Utilisez-vous uniquement des données utilisateur-agent de base ?

L'ensemble par défaut d'indicateurs client User-Agent inclut les éléments suivants:

  • Sec-CH-UA: nom du navigateur et version majeure/importante
  • Sec-CH-UA-Mobile: valeur booléenne indiquant un appareil mobile
  • Sec-CH-UA-Platform: nom du système d'exploitation
    • Notez que ce point a été mis à jour dans la spécification et sera reflété dans Chrome et les autres navigateurs basés sur Chromium prochainement.

La version réduite de la chaîne user-agent proposée conservera également ces informations de base de manière rétrocompatible. Par exemple, au lieu de Chrome/90.0.4430.85, la chaîne inclut Chrome/90.0.0.0.

Si vous ne vérifiez que le nom du navigateur, la version majeure ou le système d'exploitation dans la chaîne user-agent, votre code continuera de fonctionner, mais vous verrez probablement des avertissements d'abandon.

Bien que vous puissiez et deviez migrer vers les hints client user-agent, vous pouvez être confronté à des contraintes de code ou de ressources qui vous empêchent de le faire. La réduction des informations dans la chaîne user-agent de manière rétrocompatible vise à s'assurer que, même si le code existant reçoit des informations moins détaillées, il doit conserver les fonctionnalités de base.

Stratégie: API JavaScript côté client à la demande

Si vous utilisez actuellement navigator.userAgent, vous devez privilégier navigator.userAgentData avant de revenir à l'analyse de la chaîne user-agent.

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

Si vous vérifiez si l'utilisateur est sur un appareil mobile ou un ordinateur, utilisez la valeur booléenne mobile:

const isMobile = navigator.userAgentData.mobile;

userAgentData.brands est un tableau d'objets avec des propriétés brand et version, dans lequel le navigateur peut lister sa compatibilité avec ces marques. Vous pouvez y accéder directement en tant que tableau ou utiliser un appel some() pour vérifier si une entrée spécifique est présente:

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
}

Si vous avez besoin de l'une des valeurs d'user-agent les plus détaillées et à entropie élevée, vous devrez la spécifier et vérifier le résultat dans l'Promise renvoyée:

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

Vous pouvez également utiliser cette stratégie si vous souhaitez passer du traitement côté serveur au traitement côté client. L'API JavaScript ne nécessite pas d'accès aux en-têtes de requête HTTP. Les valeurs de l'user-agent peuvent donc être demandées à tout moment.

Stratégie: en-tête statique côté serveur

Si vous utilisez l'en-tête de requête User-Agent sur le serveur et que vos besoins en termes de données sont relativement cohérents sur l'ensemble de votre site, vous pouvez spécifier les indices client souhaités en tant qu'ensemble statique dans vos réponses. Il s'agit d'une approche relativement simple, car vous n'avez généralement besoin de la configurer qu'à un seul endroit. Par exemple, il peut se trouver dans la configuration de votre serveur Web si vous y ajoutez déjà des en-têtes, dans votre configuration d'hébergement ou dans la configuration de niveau supérieur du framework ou de la plate-forme que vous utilisez pour votre site.

Envisagez cette stratégie si vous transformez ou personnalisez les réponses diffusées en fonction des données de l'user-agent.

Les navigateurs ou d'autres clients peuvent choisir de fournir différentes suggestions par défaut. Il est donc recommandé de spécifier tout ce dont vous avez besoin, même s'il est généralement fourni par défaut.

Par exemple, les valeurs par défaut actuelles de Chrome sont représentées comme suit:

⬇️ En-têtes de réponse

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

Si vous souhaitez également recevoir le modèle de l'appareil dans les réponses, vous devez envoyer:

⬇️ En-têtes de réponse

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

Lorsque vous effectuez le traitement côté serveur, vous devez d'abord vérifier si l'en-tête Sec-CH-UA souhaité a été envoyé, puis utiliser l'analyse de l'en-tête User-Agent si celui-ci n'est pas disponible.

Stratégie: déléguer des indices aux requêtes multi-origines

Si vous demandez des sous-ressources inter-origines ou intersites qui nécessitent l'envoi d'hints client user-agent dans leurs requêtes, vous devez spécifier explicitement les hints souhaités à l'aide d'une stratégie d'autorisations.

Par exemple, imaginons que https://blog.site héberge des ressources sur https://cdn.site, qui peuvent renvoyer des ressources optimisées pour un appareil spécifique. https://blog.site peut demander l'indice Sec-CH-UA-Model, mais doit le déléguer explicitement à https://cdn.site à l'aide de l'en-tête Permissions-Policy. La liste des indices contrôlés par les règles est disponible dans le brouillon de l'infrastructure Client Hints.

⬇️ Réponse de blog.site déléguant l'indice

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

⬆️ Les requêtes aux sous-ressources sur cdn.site incluent l'indice délégué

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

Vous pouvez spécifier plusieurs indices pour plusieurs origines, et pas seulement à partir de la plage ch-ua:

⬇️ Réponse de blog.site déléguant plusieurs indices à plusieurs origines

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")

Stratégie: déléguer des indices aux iFrames

Les iFrames inter-origines fonctionnent de manière similaire aux ressources inter-origines, mais vous spécifiez les indices que vous souhaitez déléguer dans l'attribut allow.

⬇️ Réponse de blog.site

Accept-CH: Sec-CH-UA-Model

↪️ HTML pour blog.site

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

⬆️ Demande envoyée à widget.site

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

L'attribut allow de l'iFrame remplace tous les en-têtes Accept-CH que widget.site peut envoyer lui-même. Assurez-vous donc d'avoir spécifié tout ce dont le site de l'iFrame aura besoin.

Stratégie: Hints dynamiques côté serveur

Si certaines parties du parcours utilisateur nécessitent une sélection plus importante d'indices que le reste du site, vous pouvez choisir de les demander à la demande plutôt que de manière statique sur l'ensemble du site. Cette approche est plus complexe à gérer, mais si vous définissez déjà des en-têtes différents par route, elle peut être réalisable.

Il est important de se rappeler ici que chaque instance de l'en-tête Accept-CH écrase effectivement l'ensemble existant. Par conséquent, si vous définissez dynamiquement l'en-tête, chaque page doit demander l'ensemble complet d'indices requis.

Par exemple, vous pouvez avoir une section de votre site pour laquelle vous souhaitez fournir des icônes et des commandes correspondant au système d'exploitation de l'utilisateur. Pour ce faire, vous pouvez également importer Sec-CH-UA-Platform-Version pour diffuser les sous-ressources appropriées.

⬇️ En-têtes de réponse pour /blog

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

⬇️ En-têtes de réponse pour /app

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

Stratégie: Hints côté serveur requis à la première requête

Il se peut que vous ayez besoin de plus que l'ensemble d'indices par défaut pour la toute première requête. Toutefois, cela est peu probable. Assurez-vous donc d'avoir examiné le raisonnement.

La première requête signifie en réalité la toute première requête de niveau supérieur pour cette origine envoyée au cours de cette session de navigation. L'ensemble d'indices par défaut inclut le nom du navigateur avec la version majeure, la plate-forme et l'indicateur mobile. La question à se poser ici est la suivante : avez-vous besoin de données étendues lors du chargement initial de la page ?

Pour obtenir des conseils supplémentaires sur la première requête, vous avez deux options. Tout d'abord, vous pouvez utiliser l'en-tête Critical-CH. Il utilise le même format que Accept-CH, mais indique au navigateur qu'il doit immédiatement réessayer la requête si la première a été envoyée sans l'indice critique.

⬆️ Demande initiale

[With default headers]

⬇️ En-têtes de réponse

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

🔃 Le navigateur réessaie la requête initiale avec l'en-tête supplémentaire

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

Cela entraînera les frais généraux de la nouvelle tentative sur la toute première requête, mais le coût d'implémentation est relativement faible. Envoyez l'en-tête supplémentaire, et le navigateur s'occupera du reste.

Dans les cas où vous avez vraiment besoin d'indices supplémentaires lors du tout premier chargement de page, la proposition de fiabilité des indices client décrit un parcours permettant de spécifier des indices dans les paramètres au niveau de la connexion. Pour ce faire, l'extension Application-Layer Protocol Settings(ALPS) est utilisée dans TLS 1.3 afin de permettre ce transfert anticipé d'indices sur les connexions HTTP/2 et HTTP/3. Nous en sommes encore au tout début, mais si vous gérez activement vos propres paramètres TLS et de connexion, c'est le moment idéal pour contribuer.

Stratégie: compatibilité héritée

Il est possible que votre site contienne du code ancien ou tiers qui dépend de navigator.userAgent, y compris des parties de la chaîne d'user-agent qui seront réduites. À long terme, vous devez planifier la transition vers les appels navigator.userAgentData équivalents, mais il existe une solution provisoire.

UA-CH retrofill est une petite bibliothèque qui vous permet de remplacer navigator.userAgent par une nouvelle chaîne créée à partir des valeurs navigator.userAgentData demandées.

Par exemple, ce code génère une chaîne d'agent utilisateur qui inclut également l'indice "model" :

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

La chaîne obtenue affiche le modèle Pixel 5, mais affiche toujours le 92.0.0.0 réduit, car l'indice uaFullVersion n'a pas été demandé:

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

Assistance supplémentaire

Si ces stratégies ne couvrent pas votre cas d'utilisation, veuillez démarrer une discussion dans le dépôt privacy-sandbox-dev-support. Nous pourrons ainsi examiner votre problème ensemble.

Photo de Ricardo Rocha sur Unsplash