Saltar al contenido

Flags de huella digital

La identidad se controla con switches de línea de comandos de Chromium. El SDK los configura a partir de opciones con nombre; con el build abierto también puedes pasarlos como args a tu propio launcher.

La mayoría de los switches están en ambos builds. Algunos son exclusivos del build con licencia (Gratis con GitHub o Pro): la lista de switches de más abajo los marca con la revisión del motor que los introdujo. Sin una clave de licencia, el SDK ejecuta el build abierto, donde esas opciones no tienen ningún efecto.

El modelo de seed

Todo gira en torno a --fingerprint=<seed>. El seed (un entero o cualquier cadena) deriva la persona de forma determinista y, además, perturba las dos superficies renderizadas que se detallan más abajo, en la sección qué recibe farbling:

  • Mismo seed ⇒ misma identidad entre lanzamientos: un visitante que vuelve.
  • Seed nuevo ⇒ una identidad nueva y plausible.
  • El ruido de canvas y WebGL se deriva por sitio (inspirado en el farbling de Brave), así que esos hashes cambian de un dominio a otro. La persona en sí —GPU, pantalla, hardware, fuentes— es la misma en todas partes, como lo sería la de una máquina real.

¿Prefieres la identidad de una máquina real en lugar de la sintética que deriva del seed? Captura un Chrome donante con el recolector e impórtalo con --fingerprint-profile=<gzip+base64 JSON> (o con la opción fingerprint_profile / fingerprintProfile del SDK, que hace el gzip+base64 por ti). Los campos presentes en el perfil sobrescriben la persona; los ausentes recurren a la persona del seed de --fingerprint o, si no hay seed, a los valores por defecto del navegador. Ambos builds leen perfiles importados; en el build con licencia se aplican por completo desde 151 r19. Consulta la guía de Playwright para ver cómo importar un perfil capturado desde el SDK.

Qué recibe farbling y qué queda fuera a propósito

Perturbar una superficie te da no vinculabilidad a costa de coherencia, y ese intercambio no compensa en todas partes. Clearcote aplica farbling a las dos superficies donde el ruido se parece más a la variación normal del hardware y deja el resto sin perturbar, porque en esas un valor perturbado llama más la atención que uno compartido. Eso sí, sin perturbar no es lo mismo que intacto: algunos valores se siguen sustituyendo por completo con los de la persona, como detallan la tabla y la nota que la sigue. Algunos tests todavía pueden señalar el propio ruido; cuando eso importa, fingerprintNoise: false lo desactiva y conserva la identidad (consulta Cómo funciona la detección).

SuperficieRuido por seedPor qué
Canvas 2D — toDataURL, getImageData y las demás formas en que una página puede exportar un canvas (en el build con licencia, todas las vías de exportación coinciden desde 152 r21)SíEl readback ya varía entre máquinas reales según la GPU, el driver y la ruta de rasterizado, así que una perturbación pequeña queda dentro de la dispersión natural.
WebGL — readPixels, toDataURLSíEl mismo razonamiento. Con disableGpuFingerprint, readPixels devuelve los píxeles sin alterar, pero las exportaciones de un canvas WebGL (toDataURL() y similares) siguen llevando ruido: combínalo con fingerprintNoise: false para que todas las lecturas del canvas coincidan.
Audio — valores de las muestras de AudioContextNo, por diseñoLa salida de Web Audio es determinista para un pipeline dado. Perturbarla implica o bien una frecuencia de muestreo fuera de especificación (44099.99 en lugar de 44100), o bien muestras que ningún renderizador estándar produce: en ambos casos, valores que un navegador real no puede emitir, lo cual delata más que dos perfiles con el mismo hash de audio. Los escalares reportados son otro asunto y sí siguen a la persona (ver más abajo).
Client rects & métricas de textoNo, por diseñoChrome cuantiza el layout sobre una cuadrícula nativa de 1/512 px, así que cada rect real es un múltiplo exacto de 0.001953125. Un factor de escala aplicado después saca cada rect fuera de esa cuadrícula, y la geometría fuera de la cuadrícula se puede recuperar con una sola medición, por pequeño que sea el factor.
WebGPU — adaptador y límitesNoSe basa en la GPU de la persona, no en el seed, así que sigue coincidiendo con lo que reporta WebGL. Que dos APIs de GPU en una misma página nombren fabricantes distintos se lee con una sola llamada.

La consecuencia práctica para el trabajo con varias cuentas: canvas y WebGL separan tus identidades; el audio renderizado y los client rects, no. Son propiedades de la máquina subyacente: el mismo host renderiza el mismo pipeline de audio y la misma cuadrícula de layout sea cual sea el perfil cargado, igual que pasaría con dos personas distintas en hardware idéntico. Si un sitio correlaciona precisamente con esas señales, la solución son máquinas separadas, no otro switch.

Hay una excepción que conviene conocer: los tres escalares de AudioContext sí siguen a la persona. sampleRate, baseLatency y outputLatency se sirven desde la persona siempre que haya una activa (un seed de --fingerprint o un perfil importado), así que varían entre identidades en lugar de exponer el backend de audio del host. Si no hay ninguno de los dos, los tres llegan tal cual desde la máquina real. Una frecuencia que la página pide explícitamente se sigue respetando, y outputLatency sigue pasando por el cuantizador propio de Chrome, que depende de los permisos. Desde 153 r26, baseLatency también sigue el tamaño de render que pide la página, y un contexto que todavía no empezó a renderizar reporta la misma latencia que un Chrome normal.

Superficies secundarias coherentes

Más allá de las señales con ruido, el motor hace que las superficies secundarias de la persona coincidan con las de un Chrome real en la plataforma de esa persona. El SDK usa por defecto el sistema operativo de tu host como plataforma; los valores específicos de Windows que aparecen abajo se aplican a una persona de Windows.

  • Los límites de WebGL (WebGL1 + WebGL2) siguen a la GPU de la persona siempre que esta máquina pueda respaldarlos. Nunca se reporta un límite más alto del que aplica el driver real, así que en un renderizador por software algunos valores son más bajos que los de la GPU indicada.
  • UNMASKED_RENDERER / UNMASKED_VENDOR son constantes durante toda la sesión: una sola GPU en todos los sitios, que sigue a la persona, en lugar de una señal delatora por origen.
  • navigator.getBattery() reporta un equipo de escritorio conectado a la corriente (cargando, nivel 1.0, sin descarga), y navigator.connection, un perfil residencial (effectiveType 4g, rtt/downlink redondeados, saveData desactivado).
  • navigator.keyboard.getLayoutMap() devuelve un mapa US-QWERTY y, con una persona de Windows, AudioContext reporta la frecuencia de muestreo y la latencia de la pila de audio de Windows.
  • window.getScreenDetails() reporta un único monitor coherente; @media (pointer: fine) / (hover: hover) corresponden a un equipo de escritorio con mouse.
  • URLs: con una persona de Windows, new URL("C:/").protocol devuelve file: y, desde 153 r26, todas las formas en que una página puede construir una URL dan la misma respuesta. navigator.share / canShare están expuestos para coincidir con el UA de Windows.
  • WebGPU: la información del adaptador y los límites/features de navigator.gpu siguen a la misma GPU que WebGL (en el build con licencia, acotados a lo que el host puede ofrecer).
  • Configuración regional: Accept-Language, navigator.language, la lista completa de navigator.languages e Intl (hilo principal + workers) salen todos de una misma lista (acceptLanguage, o geoip). El SDK configura --accept-lang y --lang por ti; con switches directos, pasa ambos.
  • Síntesis de voz: speechSynthesis ofrece el conjunto de voces de la persona, incluidas las voces de red de Google que lista un Chrome de esa versión (153 r27). fingerprintVoices: false conserva, en cambio, las voces propias de la máquina (152 r22).
  • Hardware: la lectura de velocidad del procesador (navigator.cpuPerformance) sigue a la persona y no al procesador real (152 r20), y el límite de memoria para scripts (performance.memory.jsHeapSizeLimit) sigue la memoria que declara la persona (153 r26).
  • Códecs & dispositivos: MediaCapabilities.decodingInfo() reporta una matriz de códecs de la persona, y enumerateDevices(), un conjunto de dispositivos multimedia de la persona (ids estables por seed, etiquetas vacías antes de conceder permisos).
  • UA-CH de alta entropía: bitness=64 / wow64=false / model; con una persona de Linux, platformVersion queda vacío, tal como lo reporta el Chrome genuino en Linux (153 r26). navigator.storage.estimate() reporta una cuota de disco realista cuando defines storageQuota.

Experimental: canvas bridge con GPU real

De forma opcional, reenvía las operaciones de canvas / WebGL a un host remoto con GPU real para que las lecturas (getImageData / toDataURL / readPixels / measureText) sean coherentes con la GPU que presentas, incluso en hardware que no puede renderizarla localmente. Como reenvía las operaciones (no imágenes pregrabadas), funciona con la mayoría de los canvas, no solo con pruebas conocidas. Actívalo con --canvas-bridge-url=ws://host:port (más --no-sandbox); si no lo defines, todo es local, exactamente como antes. El servidor de render controla un navegador en el host con GPU real (un Chrome normal por CDP, o un Clearcote headless). Consulta la guía del canvas bridge para la configuración completa.

Overrides nativos de metadatos & light_stealth

Además de la persona derivada del seed, un conjunto de overrides nativos de un solo valor te permite falsear valores individuales de navigator/screen directamente: hardwareConcurrency, deviceMemory, colorDepth, devicePixelRatio, maxTouchPoints y (de forma opcional) las dimensiones de screen/avail*. Cada uno lo lee directamente su getter con la precedencia flag > persona de --fingerprint > host real, así que un valor fijado a mano gana sobre cualquier seed y los overrides funcionan con o sin un seed de --fingerprint: nunca activan la maquinaria de la persona.

El SDK agrupa un subconjunto ligero detrás de un solo flag, light_stealth: aplica uno de unos pocos paquetes de metadatos coherentes (hardwareConcurrency, deviceMemory, colorDepth, devicePixelRatio, maxTouchPoints) solo mediante esos switches nativos. El seed solo elige el paquete y no se pasa al motor, así que no hay ruido de canvas ni de WebGL, y todas las sesiones light_stealth de una misma máquina comparten el hash de canvas del host: usa un seed de fingerprint por cuenta sin lightStealth cuando esos hashes deban ser distintos. El ClientHello de TLS y la versión real del navegador no cambian. Las dimensiones de screen no se falsean por defecto, a propósito (se activan de forma opcional con screenWidth, etc.), porque una pantalla falsa que no se puede conciliar con la superficie de render real es fácil de detectar; en una pantalla sin escalado, comprueba que el devicePixelRatio elegido le corresponda, o fíjalo tú mismo. Una opción explícita siempre gana sobre el preset.

javascript
import { launch } from "clearcote";
// one coherent metadata bundle; "my-seed" only picks which one — no seed reaches the engine, so no canvas noise:
const browser = await launch({ lightStealth: true, fingerprint: "my-seed" });

// or set individual values by hand (no --fingerprint needed):
const b2 = await launch({
  hardwareConcurrency: 8,
  deviceMemory: 8,
  devicePixelRatio: 1.25,
  maxTouchPoints: 0,
});

Todos los switches

Filtra por nombre o efecto, o limita la lista a un subsistema. Haz clic en un switch para leer qué hace y ver su forma válida más corta.

Identidad y persona

Seed maestro (entero o cadena). Deriva toda la persona (GPU, pantalla, hardware, fuentes) y controla el ruido de canvas y WebGL por sitio. El idioma y la zona horaria no se derivan del seed: vienen de --accept-lang / --timezone (SDK: acceptLanguage, timezone o geoip). El audio renderizado y los client rects no se alteran; los escalares sampleRate/baseLatency/outputLatency de AudioContext se sirven desde la persona cuando hay una activa; consulta la documentación de huella digital. Mismo seed ⇒ misma identidad.

Hardware y pantalla
GPU y WebGPU
Red y configuración regional
Ruido
Canvas bridge

Ejemplos

Estos son conjuntos de flags de línea de comandos en bruto. Para flujos completos en Python y Node, consulta Ejemplos.

Una identidad de Windows completa y coherente:

bash
--fingerprint=acme-tenant-7 \
--fingerprint-platform=windows \
--fingerprint-brand=Chrome \
--accept-lang=en-US,en \
--lang=en-US \
--timezone=America/New_York \
--fingerprint-hardware-concurrency=8

Una identidad de Windows con cadenas de GPU personalizadas, en el formato en que las reporta ANGLE en Windows:

bash
--fingerprint=42 \
--fingerprint-platform=windows \
--fingerprint-gpu-vendor="Google Inc. (NVIDIA)" \
--fingerprint-gpu-renderer="ANGLE (NVIDIA, NVIDIA GeForce RTX 3060 (0x00002504) Direct3D11 vs_5_0 ps_5_0, D3D11)"

Las cadenas cambian lo que se reporta, no lo que se dibuja: los píxeles siguen saliendo de la GPU de esta máquina. Indica la GPU con la que realmente renderizas, o usa el canvas bridge. La plataforma macos cambia las cadenas de identidad (UA, client hints, navigator.platform), pero detrás no hay ningún modelo de máquina macOS, y android es una persona móvil de tipo best-effort.

Fija una ubicación junto con su zona horaria para que la geolocalización y el reloj coincidan:

bash
--fingerprint=nyc-1 \
--timezone=America/New_York \
--fingerprint-location=40.7128,-74.0060

Importa los valores de una máquina real

En lugar de un seed sintético, puedes reportar los valores exactos que reportó un Chrome real: cadenas de GPU + tabla de getParameter, pantalla, fuentes, voces, audio. Los valores se sustituyen; el renderizado lo sigue haciendo tu propia máquina, así que un perfil no les da a dos cuentas canvas distintos (eso lo consigue un seed de --fingerprint por cuenta). Toma uno de la biblioteca clearcote-profiles, una colección curada (miles de perfiles de máquinas reales, etiquetados por fabricante de GPU), o captura el tuyo con el recolector; luego cárgalo y comprueba que se cargó:

javascript
import { launch } from "clearcote";
// path to a captured .json profile, a profile object, or a JSON string
const browser = await launch({ fingerprintProfile: "./real-machine.json" });
// fields present in the profile override the persona;
// absent fields fall back to the --fingerprint seed's persona, or the browser's defaults with no seed.

Flujo de captura: abre la página del recolector en un Chrome real que quieras clonar, exporta el JSON y pásalo con fingerprintProfile (SDK) / --fingerprint-profile (motor). Verifícalo con tools/fingerprint-collect/verify_profile.py.

Qué hace cada parche

Cada switch del build abierto corresponde a un parche legible del motor: los diffs están en patches/, con un resumen de una línea de cada uno en patches/README.md. Los switches marcados como “build con licencia” vienen del conjunto de parches propio del build con licencia, que no es público.

La coherencia importa más que cualquier valor aislado: mantén la plataforma, la zona horaria, la configuración regional y la GPU plausibles en conjunto. Consulta Arquitectura para ver cómo el motor las mantiene consistentes, y Cómo funciona la detección para entender por qué esto es mejor que el spoofing desde JavaScript.