Zu User-Agent-Client-Hints migrieren

Strategien zur Migration Ihrer Website von der Nutzung des User-Agent-Strings zu den neuen User-Agent-Client-Hinweisen

Der User-Agent-String ist eine wichtige passive Fingerprinting-Oberfläche in Browsern und schwer zu verarbeiten. Es gibt jedoch viele legitime Gründe für das Erfassen und Verarbeiten von User-Agent-Daten, daher ist ein Weg zu einer besseren Lösung erforderlich. User-Agent-Client-Hints bieten sowohl eine explizite Möglichkeit, den Bedarf an User-Agent-Daten zu erklären, als auch Methoden, mit denen die Daten in einem benutzerfreundlichen Format zurückgegeben werden.

In diesem Artikel erfahren Sie, wie Sie Ihren Zugriff auf User-Agent-Daten prüfen und die Nutzung von User-Agent-Strings zu User-Agent-Client-Hinweisen migrieren.

Erhebung und Verwendung von User-Agent-Daten prüfen

Wie bei jeder Form der Datenerhebung sollten Sie immer verstehen, warum Sie diese erheben. Der erste Schritt besteht darin zu verstehen, wo und warum Sie User-Agent-Daten verwenden. Dabei spielt es keine Rolle, ob Sie eine Aktion ausführen oder nicht.

Wenn Sie nicht wissen, ob oder wo User-Agent-Daten verwendet werden, können Sie im Frontend-Code nach navigator.userAgent und im Backend-Code nach dem HTTP-Header User-Agent suchen. Prüfen Sie außerdem, ob im Frontend-Code bereits verworfene Funktionen wie navigator.platform und navigator.appVersion verwendet werden.

Stellen Sie sich aus funktioneller Sicht die Bereiche im Code vor, an denen Sie Daten aufzeichnen oder verarbeiten:

  • Browsername oder -version
  • Name oder Version des Betriebssystems
  • Marke oder Modell des Geräts
  • CPU-Typ, -Architektur oder -Bit (z. B. 64-Bit)

Es ist auch wahrscheinlich, dass Sie zur Verarbeitung des User-Agents eine Bibliothek oder einen Dienst eines Drittanbieters verwenden. Prüfen Sie in diesem Fall, ob sie aktualisiert werden, um User-Agent-Client-Hints zu unterstützen.

Verwendest du nur grundlegende User-Agent-Daten?

Der Standardsatz von User-Agent-Client-Hints umfasst:

  • Sec-CH-UA: Browsername und Hauptversion/Hauptversion
  • Sec-CH-UA-Mobile: boolescher Wert, der ein Mobilgerät angibt
  • Sec-CH-UA-Platform: Name des Betriebssystems

In der reduzierten Version des vorgeschlagenen User-Agent-Strings werden diese grundlegenden Informationen ebenfalls abwärtskompatibel gespeichert. Statt Chrome/90.0.4430.85 würde der String beispielsweise Chrome/90.0.0.0 enthalten.

Wenn Sie den User-Agent-String lediglich auf den Browsernamen, die Hauptversion oder das Betriebssystem prüfen, funktioniert der Code weiterhin, auch wenn wahrscheinlich Einstellungswarnungen angezeigt werden.

Sie können und sollten zu User-Agent-Client-Hinweisen migrieren. Möglicherweise gibt es aber Legacy-Code- oder Ressourceneinschränkungen, die dies verhindern. Durch diese abwärtskompatible Reduzierung der Informationen im User-Agent-String soll dafür gesorgt werden, dass vorhandener Code zwar weniger detaillierte Informationen erhält, aber die grundlegenden Funktionen beibehalten sollte.

Strategie: Clientseitige On-Demand-JavaScript-API

Wenn Sie derzeit navigator.userAgent verwenden, sollten Sie navigator.userAgentData bevorzugen, bevor Sie auf das Parsen des User-Agent-Strings zurückgreifen.

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

Verwenden Sie bei der Prüfung auf Mobilgeräten oder Computern den booleschen Wert mobile:

const isMobile = navigator.userAgentData.mobile;

userAgentData.brands ist ein Array von Objekten mit brand- und version-Attributen, anhand derer der Browser seine Kompatibilität mit diesen Marken auflisten kann. Sie können direkt als Array darauf zugreifen oder mit einem some()-Aufruf prüfen, ob ein bestimmter Eintrag vorhanden ist:

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
}

Wenn du einen der detaillierteren User-Agent-Werte mit hoher Entropie benötigst, musst du ihn angeben und im zurückgegebenen Promise nach dem Ergebnis suchen:

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

Sie können diese Strategie auch verwenden, wenn Sie von der serverseitigen Verarbeitung zur clientseitigen Verarbeitung wechseln möchten. Die JavaScript API benötigt keinen Zugriff auf HTTP-Anfrageheader. User-Agent-Werte können also jederzeit angefordert werden.

Strategie: Statischer serverseitiger Header

Wenn Sie den User-Agent-Anfrageheader auf dem Server verwenden und Ihre Anforderungen an diese Daten auf der gesamten Website relativ einheitlich sind, können Sie die gewünschten Clienthinweise als statischen Satz in Ihren Antworten angeben. Dies ist ein relativ einfacher Ansatz, da Sie ihn in der Regel nur an einem Standort konfigurieren müssen. Es kann beispielsweise in Ihrer Webserverkonfiguration vorhanden sein, wenn Sie dort bereits Header, Ihre Hostingkonfiguration oder die Konfiguration der obersten Ebene des Frameworks oder der Plattform, die Sie für Ihre Website verwenden, hinzufügen.

Verwenden Sie diese Strategie, wenn Sie die basierend auf den User-Agent-Daten ausgegebenen Antworten transformieren oder anpassen.

Browser oder andere Clients können unterschiedliche Standardhinweise bereitstellen. Daher empfiehlt es sich, alles anzugeben, was Sie benötigen, selbst wenn es standardmäßig standardmäßig bereitgestellt wird.

Die aktuellen Standardeinstellungen für Chrome würden beispielsweise so dargestellt:

⬇️ Antwortheader

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

Wenn Sie das Gerätemodell auch als Antworten erhalten möchten, senden Sie Folgendes:

⬇️ Antwortheader

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

Bei der serverseitigen Verarbeitung sollten Sie zuerst prüfen, ob der gewünschte Sec-CH-UA-Header gesendet wurde, und dann auf den User-Agent-Header zurückgreifen, wenn er nicht verfügbar ist.

Strategie: Hinweise an ursprungsübergreifende Anfragen delegieren

Wenn Sie ursprungs- oder websiteübergreifende Unterressourcen anfordern, für die User-Agent-Client-Hinweise für die Anfragen gesendet werden müssen, müssen Sie die gewünschten Hinweise mithilfe einer Berechtigungsrichtlinie explizit angeben.

Beispiel: https://blog.site hostet Ressourcen unter https://cdn.site, die für ein bestimmtes Gerät optimierte Ressourcen zurückgeben können. https://blog.site kann den Hinweis Sec-CH-UA-Model anfordern, muss ihn aber mithilfe des Headers Permissions-Policy explizit an https://cdn.site delegieren. Die Liste der richtliniengesteuerten Hinweise ist im Infrastrukturentwurf für Kundenhinweise verfügbar.

⬇️ Antwort von blog.site mit delegiertem Hinweis

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

⬆️ Anfrage an Unterressourcen auf cdn.site enthält den delegierten Hinweis

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

Du kannst mehrere Hinweise für mehrere Ursprünge angeben, nicht nur aus dem Bereich ch-ua:

⬇️ Antwort von blog.site, bei der mehrere Hinweise an mehrere Ursprünge delegiert werden

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

Strategie: Hinweise an iFrames delegieren

Ursprungsübergreifende iFrames funktionieren ähnlich wie ursprungsübergreifende Ressourcen, aber du gibst die zu delegierenden Hinweise im Attribut allow an.

⬇️ Antwort von blog.site

Accept-CH: Sec-CH-UA-Model

↪️ HTML für blog.site

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

⬆️ Anfrage an widget.site

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

Das Attribut allow im iFrame überschreibt jeden Accept-CH-Header, den widget.site möglicherweise selbst sendet. Achte also darauf, dass du alles angegeben hast, was für den iFrame der Website benötigt wird.

Strategie: Dynamische serverseitige Hinweise

Wenn bestimmte Teile der User Journey eine größere Auswahl an Hinweisen als für den Rest der Website benötigen, können Sie diese Hinweise bei Bedarf statt statisch für die gesamte Website anfordern. Dies ist komplexer zu verwalten, aber wenn Sie bereits unterschiedliche Header für einzelne Routen festgelegt haben, ist dies möglicherweise möglich.

Denken Sie daran, dass jede Instanz des Accept-CH-Headers den vorhandenen Satz effektiv überschreibt. Wenn Sie den Header dynamisch festlegen, muss jede Seite alle erforderlichen Hinweise anfordern.

Angenommen, Sie möchten in einem Bereich Ihrer Website Symbole und Steuerelemente zur Verfügung stellen, die dem Betriebssystem des Nutzers entsprechen. Dazu können Sie zusätzlich Sec-CH-UA-Platform-Version abrufen, um geeignete Unterressourcen bereitzustellen.

⬇️ Antwortheader für /blog

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

⬇️ Antwortheader für /app

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

Strategie: Serverseitige Hinweise bei der ersten Anfrage erforderlich

Es kann vorkommen, dass Sie bei der ersten Anfrage mehr als den Standardsatz von Hinweisen benötigen. Dies kommt jedoch eher selten vor. Lesen Sie sich die Begründung daher gut durch.

Die erste Anfrage ist wirklich die allererste Anfrage der obersten Ebene für diesen Ursprung, die in dieser Browsersitzung gesendet wurde. Der Standardsatz von Hinweisen umfasst den Browsernamen mit der Hauptversion, die Plattform und die Mobilgeräteanzeige. Die Frage, die Sie sich hier stellen sollten, lautet: Benötigen Sie beim ersten Seitenaufbau erweiterte Daten?

Für zusätzliche Hinweise bei der ersten Anfrage gibt es zwei Optionen. Als Erstes können Sie den Header Critical-CH verwenden. Diese hat das gleiche Format wie Accept-CH, teilt dem Browser jedoch mit, dass die Anfrage sofort wiederholt werden soll, wenn die erste Anfrage ohne den kritischen Hinweis gesendet wurde.

⬆️ Erste Anfrage

[With default headers]

⬇️ Antwortheader

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

🔃 Der Browser wiederholt die erste Anfrage mit dem zusätzlichen Header

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

Dies verursacht zwar den Aufwand der Wiederholung bei der ersten Anfrage, die Implementierungskosten sind jedoch relativ gering. Senden Sie den zusätzlichen Header und der Browser erledigt den Rest.

In Situationen, in denen Sie beim ersten Laden der Seite unbedingt zusätzliche Hinweise benötigen, legt der Vorschlag zur Zuverlässigkeit von Clienthinweisen eine Route für die Angabe von Hinweisen in den Einstellungen auf Verbindungsebene vor. Dadurch wird die Erweiterung Application-Layer Protocol Settings(ALPS) für TLS 1.3 verwendet, um diese frühzeitige Weitergabe von Hinweisen bei HTTP/2- und HTTP/3-Verbindungen zu ermöglichen. Dies befindet sich noch in einem frühen Stadium, aber wenn Sie Ihre eigenen TLS- und Verbindungseinstellungen aktiv verwalten, ist dies ein idealer Zeitpunkt, um einen Beitrag zu leisten.

Strategie: Legacy-Unterstützung

Möglicherweise befindet sich auf deiner Website Legacy- oder Drittanbietercode, der von navigator.userAgent abhängig ist. Dazu gehören auch Teile des User-Agent-Strings, die reduziert werden. Langfristig sollten Sie zu den entsprechenden navigator.userAgentData-Aufrufen wechseln. Es gibt jedoch eine Übergangslösung.

UA-CH retrofill ist eine kleine Bibliothek, mit der Sie navigator.userAgent mit einem neuen String aus den angeforderten navigator.userAgentData-Werten überschreiben können.

Mit diesem Code wird beispielsweise ein User-Agent-String generiert, der zusätzlich den Hinweis „model“ enthält:

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

Der resultierende String würde das Modell Pixel 5 zeigen, aber dennoch den reduzierten 92.0.0.0-Wert, da der uaFullVersion-Hinweis nicht angefordert wurde:

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

Weitere Unterstützung

Wenn diese Strategien Ihren Anwendungsfall nicht abdecken, starten Sie bitte eine Diskussion im Repository „privacy-sandbox-dev-support“, damit wir das Problem gemeinsam untersuchen können.

Foto von Ricardo Rocha auf Unsplash