AntiDetect
Funktionen Technologie Preise Für wen
Doks Blog FAQ
Sprache
EnglishItalianoDeutschEspañolРусский
Download

Der User Agent ist umgezogen, und die Hälfte des Spoofings ist nicht mitgekommen

Chrome hat den User-Agent-String eingefroren und die echten Informationen in die Client Hints verlegt. Viele Setups schreiben weiterhin den alten String um und lassen navigator.userAgentData die Wahrheit melden — ein Widerspruch, den jede Seite für den Preis eines await bekommt.

Warum der User Agent eingefroren wurde

Der User-Agent-String war zu einem Fossilienfund geworden. Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/147.0.0.0 Safari/537.36 behauptet, Mozilla, Safari und KHTML zu sein — nichts davon stimmt, aus Kompatibilitätsgründen, die in die 1990er zurückreichen.

Er war außerdem eine passive Fingerprinting-Fläche: Jede Anfrage funkte OS-Version, CPU-Architektur und den exakten Browser-Build an jeden Dritten auf der Seite, ob das jemand brauchte oder nicht.

Googles Antwort waren die User-Agent Client Hints. Den alten String gibt es weiterhin, er wird aber schrittweise eingefroren — beachte, dass Windows NT 10.0 inzwischen auch von Windows 11 gemeldet wird und die Felder der Nebenversion auf 0.0.0 festgenagelt sind. Die echten Details sind auf eine neue Fläche gewandert, aufgeteilt in zwei Stufen danach, wie identifizierend sie sind.

Niedrige und hohe Entropie

Hints mit niedriger Entropie werden ungefragt bei jeder Anfrage mitgeschickt, weil sie wenig verraten:

Hints mit hoher Entropie bleiben zurück, bis ein Server sie über einen Accept-CH-Antwortheader anfordert oder bis ein Skript danach fragt:

Diese Schranke war als Datenschutzkontrolle gedacht. In der Praxis händigt Chrome in First-Party-Kontexten alles Angeforderte ohne Nachfrage und ohne sichtbares Signal aus, sodass ein Fingerprinting-Skript einfach getHighEntropyValues() mit sämtlichen Feldnamen aufruft und alles bekommt.

Wo Profile sich selbst widersprechen

Drei Fehler tauchen immer wieder auf, und alle drei sind mit einer Zeile zu erkennen.

Der String wurde geändert, das Objekt nicht

Der mit Abstand häufigste. Ein Werkzeug schreibt navigator.userAgent so um, dass es Chrome 147 auf macOS behauptet, und lässt navigator.userAgentData weiter Chrome 131 unter Windows melden. Die Prüfung:

const ua = navigator.userAgent;
const d  = await navigator.userAgentData.getHighEntropyValues(
  ['platform','platformVersion','architecture','fullVersionList']);
// ua says "Macintosh; Intel Mac OS X 10_15_7"
// d.platform === "Windows"   →  contradiction, no ambiguity

Windows 10 oder Windows 11

Da der UA-String für beide Windows NT 10.0 meldet, lassen sie sich nur über platformVersion unterscheiden: Werte unter 13 bedeuten Windows 10, 13 und darüber Windows 11. Ein Profil, das in jeder Marketing-Hinsicht Windows 11 behauptet, aber platformVersion: "10.0.0" zurückgibt, behauptet dort, wo es zählt, Windows 10 — und sein Schriftartensatz wird als Nächstes nach Segoe UI Variable gefragt.

Die Markenliste stimmt nicht

Der GREASE-Eintrag — die absichtlich erfundene Marke wie "Not-A.Brand" — existiert, damit Server die Liste nicht fest verdrahten. Sein genauer Text und seine Position ändern sich zwischen Chrome-Versionen, und die Zeichensetzung hat sich über die Zeit gewandelt: Not:A-Brand, Not.A/Brand, Not-A.Brand, mit Version 8, 24 oder 99 je nach Release. Eine aus einem älteren Build kopierte Markenliste ist ein Versionswiderspruch, der offen neben einem neueren User Agent steht.

Das Worker-Problem, schon wieder

navigator.userAgentData existiert auch in Web Workern und Service Workern. Diese Scopes baut die Engine auf eigenen Threads — und wie im CDP-Artikel beschrieben, erreicht ein Override auf Seitenebene sie nicht.

// main thread: brands say 147
// worker: brands say whatever the binary really is
new Worker(URL.createObjectURL(new Blob([`
  navigator.userAgentData.getHighEntropyValues(['fullVersionList','platform'])
    .then(v => postMessage(v))
`], { type: 'text/javascript' })));

Jede Abweichung hier ist nicht bloß verdächtig, sondern eindeutig. Es gibt keine Browserkonfiguration, in der Fenster und Worker legitim unterschiedliche Markenlisten melden, weil beide dieselbe Quelle lesen.

Das ist derselbe strukturelle Punkt, der sich durch all diese Artikel zieht, und er sei noch einmal klar gesagt: Ein Wert, über den die Engine entscheidet, lässt sich nur in der Engine überzeugend ändern.

Header und JavaScript müssen auch zusammenpassen

UA-CH existiert an zwei Orten gleichzeitig — in HTTP-Headern und in einem JavaScript-Objekt — und beide werden von verschiedenen Teilen des Browsers erzeugt. Ein Werkzeug, das die eine Seite patcht und die andere nicht, erzeugt einen Widerspruch, der im Serverlog sichtbar ist, ganz ohne clientseitiges Skript.

Die Paare, die übereinstimmen müssen:

KopfzeileJavaScript
Sec-CH-UAuserAgentData.brands
Sec-CH-UA-PlatformuserAgentData.platform
Sec-CH-UA-MobileuserAgentData.mobile
Sec-CH-UA-Platform-VersionplatformVersion
Sec-CH-UA-Full-Version-ListfullVersionList
User-Agentnavigator.userAgent

Sechs Paare, und das letzte bekommt jeder hin. Eine nützliche Disziplin ist, alle aus einem einzigen Profildatensatz abzuleiten, statt sie als getrennte Felder zu konfigurieren — dasselbe Argument wie im Konsistenz-Artikel, auf eine konkrete Fläche angewandt.

Sechs HTTP-Client-Hint-Header, jeweils mit ihrem JavaScript-Gegenstück gepaart, eines der Paare gebrochen
Sechs Paare, erzeugt von verschiedenen Teilen des Browsers. Patch die eine Seite, und die andere widerspricht ihr — sichtbar im Serverlog, ganz ohne clientseitiges Skript.

Ein Profil prüfen

Führe das in dem Profil aus, das du prüfen willst, und lies jede Zeile gegen den User Agent darüber:

(async () => {
  const d = await navigator.userAgentData.getHighEntropyValues(
    ['architecture','bitness','model','platformVersion','fullVersionList','wow64']);
  console.log(navigator.userAgent);
  console.log(navigator.userAgentData.platform, navigator.userAgentData.mobile);
  console.log(navigator.userAgentData.brands);
  console.log(d);
})();

Vier Fragen an die Ausgabe:

  1. Stimmt die Plattform im Objekt mit der Plattform im String überein?
  2. Stimmt die Hauptversion in fullVersionList mit dem Chrome/NNN im String überein?
  3. Passt platformVersion zum Betriebssystem, das du behaupten willst — 13 oder höher für Windows 11?
  4. Liefert ein Worker dieselben Antworten?

browserleaks.com/client-hints zeigt Header- und JavaScript-Seite nebeneinander, was den Vergleich der sechs Paare auf einen Blick erlaubt. Das ist der schnellste Weg, einen halb angewandten Spoof zu finden.

Eine Versionsnummer, überall

P8 leitet UA-String, Client-Hint-Header und userAgentData aus demselben Profil ab — auch in Workern, die eingeschleuste Skripte nicht erreichen.

Kostenlos starten
Alle Artikel Weiter: Deine Schriftartenliste ist ein OS-Geständnis