AntiDetect
Funzionalità Tecnologia Prezzi Per chi è
Docs Blog FAQ
Lingua
EnglishItalianoDeutschEspañolРусский
Scarica

Lo User Agent si è spostato, e metà dello spoofing non l'ha seguito

Chrome ha congelato la stringa dello user agent e ha spostato l'informazione vera nei Client Hints. Molte configurazioni riscrivono ancora la vecchia stringa e lasciano che navigator.userAgentData dica la verità — una contraddizione a disposizione di qualsiasi pagina al costo di un solo await.

Perché lo user agent è stato congelato

La stringa dello user agent era diventata un reperto fossile. Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/147.0.0.0 Safari/537.36 dichiara di essere Mozilla, Safari e KHTML, e nessuna delle tre è vera, per ragioni di compatibilità che risalgono agli anni Novanta.

Era anche una superficie di fingerprinting passivo: ogni richiesta trasmetteva la versione del sistema operativo, l'architettura della CPU e la build esatta del browser a ogni terza parte presente nella pagina, che servisse a qualcuno o no.

La risposta di Google sono stati gli User-Agent Client Hints. La vecchia stringa esiste ancora ma viene progressivamente congelata — nota che Windows NT 10.0 ora viene riportato anche da Windows 11, e i campi di versione minore sono fissati a 0.0.0. Il dettaglio vero si è spostato su una nuova superficie, divisa in due livelli a seconda di quanto identifichi.

Bassa entropia e alta entropia

Gli hint a bassa entropia vengono inviati a ogni richiesta senza che nessuno li chieda, perché rivelano poco:

Gli hint ad alta entropia vengono trattenuti finché un server non li richiede con un header di risposta Accept-CH, oppure finché non li chiede uno script:

Questo filtro è stato pensato come controllo per la privacy. In pratica, nei contesti di prima parte Chrome consegna tutto ciò che viene richiesto senza alcun prompt e senza alcun segnale visibile, quindi a uno script di fingerprinting basta chiamare getHighEntropyValues() con tutti i nomi dei campi per ottenerli tutti.

Dove i profili si contraddicono

Tre errori ricorrono di continuo, e tutti e tre si rilevano con una riga.

La stringa è stata cambiata e l'oggetto no

Il più comune in assoluto. Uno strumento riscrive navigator.userAgent per dichiarare Chrome 147 su macOS, e lascia navigator.userAgentData a dire Chrome 131 su Windows. Il controllo:

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 o Windows 11

Dato che la stringa UA riporta Windows NT 10.0 per entrambi, l'unico modo per distinguerli è platformVersion: valori sotto 13 indicano Windows 10, 13 e oltre indicano Windows 11. Un profilo che dichiara Windows 11 in ogni senso di facciata ma restituisce platformVersion: "10.0.0" sta dichiarando Windows 10 dove conta — e al suo insieme di font verrà chiesto conto di Segoe UI Variable subito dopo.

L'elenco dei brand è sbagliato

La voce GREASE — il brand deliberatamente falso come "Not-A.Brand" — esiste per impedire ai server di codificare l'elenco a mano. Il suo testo esatto e la sua posizione cambiano fra le versioni di Chrome, e la punteggiatura è variata nel tempo: Not:A-Brand, Not.A/Brand, Not-A.Brand, con la versione a 8, 24 o 99 a seconda del rilascio. Un elenco di brand copiato da una build più vecchia è un'incoerenza di versione in bella vista accanto a uno user agent più recente.

Il problema dei worker, di nuovo

navigator.userAgentData esiste dentro i Web Worker e i Service Worker. Quegli scope sono costruiti dal motore sui loro thread e — come visto nell'articolo su CDP — un override a livello di pagina non li raggiunge.

// 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' })));

Qualsiasi discordanza qui è decisiva, non solo sospetta. Non esiste configurazione del browser in cui la finestra e un worker riportino legittimamente elenchi di brand diversi, perché entrambi leggono la stessa sorgente.

È lo stesso punto strutturale che ritorna in tutti questi articoli, e vale la pena dirlo ancora una volta chiaramente: un valore deciso dal motore può essere cambiato in modo convincente solo nel motore.

Anche header e JavaScript devono concordare

UA-CH esiste in due posti contemporaneamente — negli header HTTP e in un oggetto JavaScript — e sono prodotti da parti diverse del browser. Uno strumento che corregge l'uno e non l'altro crea una contraddizione visibile in un log del server senza alcuno script lato client.

Le coppie che devono coincidere:

IntestazioneJavaScript
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

Sei coppie, e l'ultima è quella che tutti azzeccano. Una disciplina utile è ricavarle tutte da un unico record di profilo invece di configurarle come campi separati — lo stesso argomento dell'articolo sulla coerenza, applicato a una superficie specifica.

Sei header HTTP di Client Hint accoppiati ai rispettivi equivalenti JavaScript, con una coppia rotta
Sei coppie prodotte da parti diverse del browser. Correggi un lato e l'altro lo contraddice — visibile in un log del server senza alcuno script lato client.

Fare l'audit di un profilo

Esegui questo nel profilo che vuoi controllare e leggi ogni riga confrontandola con lo user agent sopra di essa:

(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);
})();

Quattro domande da porre all'output:

  1. La piattaforma nell'oggetto corrisponde a quella nella stringa?
  2. La versione maggiore in fullVersionList corrisponde a Chrome/NNN nella stringa?
  3. platformVersion è coerente con il sistema operativo che intendi dichiarare — 13 o superiore per Windows 11?
  4. Un worker restituisce le stesse risposte?

browserleaks.com/client-hints mostra insieme il lato header e quello JavaScript, il che rende facile confrontare a colpo d'occhio le sei coppie qui sopra. È il modo più rapido per trovare uno spoof applicato a metà.

Un solo numero di versione, ovunque

P8 ricava la stringa UA, gli header dei Client Hint e userAgentData dallo stesso profilo — anche dentro i worker, dove gli script iniettati non arrivano.

Inizia gratis
Tutti gli articoli Prossimo: la tua lista di font è una confessione sul sistema operativo