AntiDetect
Funciones Tecnología Precios Para quién
Docs Blog FAQ
Idioma
EnglishItalianoDeutschEspañolРусский
Descargar

El User Agent se mudó, y la mitad del spoofing no le siguió

Chrome congeló la cadena del user agent y trasladó la información real a los Client Hints. Muchas configuraciones siguen reescribiendo la cadena antigua y dejan que navigator.userAgentData diga la verdad — una contradicción al alcance de cualquier página por el precio de un await.

Por qué se congeló el user agent

La cadena del user agent se había convertido en un registro fósil. Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/147.0.0.0 Safari/537.36 dice ser Mozilla, Safari y KHTML, y ninguna de las tres es cierta, por razones de compatibilidad que vienen de los años noventa.

Era además una superficie de fingerprinting pasivo: cada petición emitía la versión del sistema operativo, la arquitectura de CPU y la build exacta del navegador a todos los terceros de la página, los necesitara alguien o no.

La respuesta de Google fueron los User-Agent Client Hints. La cadena antigua sigue existiendo pero se congela de forma progresiva — fíjate en que Windows NT 10.0 ahora lo informa también Windows 11, y los campos de versión menor están fijados a 0.0.0. El detalle real se mudó a una superficie nueva, dividida en dos niveles según lo identificativa que sea.

Baja entropía y alta entropía

Los hints de baja entropía se envían en cada petición sin que nadie los pida, porque revelan poco:

Los hints de alta entropía se retienen hasta que un servidor los solicita con una cabecera de respuesta Accept-CH, o hasta que un script los pide:

Ese filtro se diseñó como control de privacidad. En la práctica, en contextos de primera parte Chrome entrega todo lo solicitado sin aviso ni señal visible, así que a un script de fingerprinting le basta con llamar a getHighEntropyValues() con todos los nombres de campo para obtenerlos todos.

Dónde se contradicen los perfiles

Tres fallos aparecen una y otra vez, y los tres se detectan con una línea.

Se cambió la cadena y no el objeto

El más común con diferencia. Una herramienta reescribe navigator.userAgent para decir Chrome 147 en macOS y deja navigator.userAgentData informando Chrome 131 en Windows. La comprobación:

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

Como la cadena UA informa Windows NT 10.0 para ambos, la única forma de distinguirlos es platformVersion: valores por debajo de 13 significan Windows 10, y 13 o superior, Windows 11. Un perfil que dice ser Windows 11 en todo lo aparente pero devuelve platformVersion: "10.0.0" está diciendo Windows 10 donde importa — y a su conjunto de fuentes le preguntarán por Segoe UI Variable a continuación.

La lista de marcas está mal

La entrada GREASE — la marca deliberadamente falsa, como "Not-A.Brand" — existe para que los servidores no fijen la lista a mano. Su texto exacto y su posición cambian entre versiones de Chrome, y la puntuación ha variado con el tiempo: Not:A-Brand, Not.A/Brand, Not-A.Brand, con la versión en 8, 24 o 99 según el lanzamiento. Una lista de marcas copiada de una build antigua es un desajuste de versión a plena vista junto a un user agent más nuevo.

El problema de los workers, otra vez

navigator.userAgentData existe dentro de los Web Workers y los Service Workers. Esos ámbitos los construye el motor en sus propios hilos y, como se vio en el artículo sobre CDP, un override a nivel de página no llega hasta ellos.

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

Cualquier desacuerdo aquí es concluyente, no solo sospechoso. No hay configuración de navegador en la que la ventana y un worker informen legítimamente listas de marcas distintas, porque ambos leen la misma fuente.

Es el mismo punto estructural que se repite en todos estos artículos, y conviene decirlo una vez más con claridad: un valor que decide el motor solo puede cambiarse de forma convincente en el motor.

Las cabeceras y JavaScript también tienen que cuadrar

UA-CH existe en dos sitios a la vez — cabeceras HTTP y un objeto JavaScript — y los producen partes distintas del navegador. Una herramienta que parchea uno y no el otro crea una contradicción visible en un log de servidor sin ningún script en el cliente.

Los pares que deben coincidir:

CabeceraJavaScript
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

Seis pares, y el último lo acierta todo el mundo. Una disciplina útil es derivarlos todos de un único registro de perfil en lugar de configurarlos como campos sueltos — el mismo argumento de el artículo sobre coherencia, aplicado a una superficie concreta.

Seis cabeceras HTTP de Client Hints emparejadas con sus equivalentes en JavaScript, con un par roto
Seis pares producidos por partes distintas del navegador. Parchea un lado y el otro lo contradice — visible en un log de servidor sin ningún script en el cliente.

Auditar un perfil

Ejecuta esto en el perfil que quieras comprobar y lee cada línea contra el user agent que tiene encima:

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

Cuatro preguntas que hacerle a la salida:

  1. ¿La plataforma del objeto coincide con la plataforma de la cadena?
  2. ¿La versión mayor de fullVersionList coincide con el Chrome/NNN de la cadena?
  3. ¿platformVersion es coherente con el sistema operativo que quieres declarar — 13 o superior para Windows 11?
  4. ¿Un worker devuelve las mismas respuestas?

browserleaks.com/client-hints muestra a la vez el lado de las cabeceras y el de JavaScript, lo que hace fácil comparar de un vistazo los seis pares anteriores. Es la forma más rápida de encontrar un spoof aplicado a medias.

Un número de versión, en todas partes

P8 deriva la cadena UA, las cabeceras de Client Hints y userAgentData del mismo perfil — también dentro de los workers, donde los scripts inyectados no llegan.

Empieza gratis
Todos los artículos Siguiente: tu lista de fuentes es una confesión sobre tu SO