Pular para o conteúdo

Flags de fingerprint

A identidade é controlada por switches de linha de comando do Chromium. O SDK os define a partir de opções nomeadas; com o build aberto, você também pode passá-los como args para o seu próprio launcher.

A maioria dos switches existe nos dois builds. Alguns são exclusivos do build licenciado (Grátis com GitHub ou Pro) — a lista de switches abaixo os marca com a revisão do motor que os adicionou. Sem uma chave de licença, o SDK roda o build aberto, no qual essas opções não têm efeito.

O modelo de seed

Tudo parte de --fingerprint=<seed>. A seed (um inteiro ou qualquer string) deriva a persona de forma determinística — e perturba as duas superfícies renderizadas listadas mais abaixo, em o que recebe farbling:

  • Mesma seed ⇒ mesma identidade entre uma inicialização e outra — um visitante que volta.
  • Nova seed ⇒ uma identidade nova e plausível.
  • O ruído de canvas e WebGL é derivado por site (inspirado no farbling do Brave), então esses hashes mudam de um domínio para outro. A persona em si — GPU, tela, hardware, fontes — é a mesma em todo lugar, como seria a de uma máquina real.

Prefere a identidade de uma máquina real à identidade sintética derivada da seed? Capture um Chrome doador com o coletor e importe-o via --fingerprint-profile=<gzip+base64 JSON> (ou pela opção fingerprint_profile / fingerprintProfile do SDK, que faz o gzip+base64 por você). Os campos presentes no perfil sobrescrevem a persona; os ausentes recorrem à persona da seed de --fingerprint, ou aos padrões do navegador quando não há seed. Os dois builds leem perfis importados; no build licenciado, eles se aplicam por completo a partir do 151 r19. Veja o guia do Playwright para saber como importar um perfil capturado pelo SDK.

O que recebe farbling e o que, de propósito, não recebe

Perturbar uma superfície traz desvinculação entre identidades, mas custa coerência, e essa troca não compensa em todo lugar. O Clearcote aplica farbling às duas superfícies em que o ruído fica mais perto da variação normal de hardware, e deixa o resto sem perturbação — porque, nelas, um valor perturbado chama mais atenção do que um valor compartilhado. Sem perturbação não quer dizer intocado, porém: alguns valores ainda são substituídos por inteiro a partir da persona, como a tabela e a nota abaixo dela detalham. Alguns verificadores ainda conseguem sinalizar o próprio ruído; quando isso importa, fingerprintNoise: false o desliga e mantém a identidade (veja Como a detecção funciona).

SuperfícieRuído por seedPor quê
Canvas 2D — toDataURL, getImageData e as outras formas de uma página exportar um canvas (no build licenciado, todos os caminhos de exportação concordam a partir do 152 r21)SimA leitura de pixels já varia entre máquinas reais conforme a GPU, o driver e o caminho de rasterização, então uma pequena perturbação fica dentro da dispersão natural.
WebGL — readPixels, toDataURLSimMesmo raciocínio. Com disableGpuFingerprint, readPixels retorna os pixels reais, mas as exportações de um canvas WebGL (toDataURL() e afins) continuam com ruído — combine com fingerprintNoise: false para que toda leitura do canvas concorde.
Áudio — valores das amostras do AudioContextNão — por designA saída do Web Audio é determinística para um mesmo pipeline. Perturbá-la significa ou uma taxa de amostragem fora da especificação (44099.99 em vez de 44100), ou amostras que nenhum renderizador padrão produz — nos dois casos, valores que um navegador real não consegue emitir, o que denuncia mais do que dois perfis compartilharem um hash de áudio. Os escalares reportados são outra história e seguem, sim, a persona — veja abaixo.
Client rects & métricas de textoNão — por designO Chrome quantiza o layout numa grade nativa de 1/512 px, então todo rect honesto é um múltiplo exato de 0.001953125. Um fator de escala aplicado depois empurra todos os rects para fora dessa grade, e uma geometria fora da grade se revela numa única medição, por menor que seja o fator.
WebGPU — adapter e limitesNãoSegue a GPU da persona, e não a seed, então continua de acordo com o que o WebGL reporta. Duas APIs de GPU na mesma página apontando fabricantes diferentes é algo que se lê numa única chamada.

A consequência prática para quem trabalha com várias contas: canvas e WebGL separam suas identidades; áudio renderizado e client rects, não. Essas são propriedades da máquina por baixo — o mesmo host renderiza o mesmo pipeline de áudio e a mesma grade de layout seja qual for o perfil carregado, exatamente como aconteceria com duas pessoas diferentes em hardware idêntico. Se um alvo correlaciona justamente por essas superfícies, a resposta são máquinas separadas, não outro switch.

Uma exceção que vale conhecer: os três escalares do AudioContext seguem, sim, a persona. sampleRate, baseLatency e outputLatency vêm da persona sempre que há uma ativa — uma seed de --fingerprint ou um perfil importado — e por isso variam de uma identidade para outra, em vez de expor o backend de áudio do host. Sem nenhum dos dois, os três vêm direto da máquina real. Uma taxa que a página pede explicitamente continua sendo respeitada, e outputLatency continua passando pelo quantizador do próprio Chrome, que depende de permissão. A partir do 153 r26, baseLatency também acompanha o tamanho de renderização que a página pede, e um contexto que ainda não começou a renderizar reporta a mesma latência que um Chrome comum.

Superfícies secundárias coerentes

Além dos sinais com ruído, o motor mantém as superfícies secundárias da persona de acordo com um Chrome real na plataforma da persona. Por padrão, o SDK usa o sistema operacional do seu host como plataforma; os valores específicos do Windows abaixo valem para uma persona Windows.

  • Os limites de WebGL (WebGL1 + WebGL2) seguem a GPU da persona sempre que esta máquina consegue sustentá-los. Um limite nunca é reportado acima do que o driver real impõe; por isso, num renderizador por software, alguns valores ficam abaixo dos da GPU indicada.
  • UNMASKED_RENDERER / UNMASKED_VENDOR são constantes durante a sessão — uma única GPU em todos os sites, acompanhando a persona, em vez de um indício que muda por origem.
  • navigator.getBattery() reporta um desktop ligado na tomada (carregando, nível 1.0, sem descarga), e navigator.connection, um perfil residencial (effectiveType 4g, rtt/downlink arredondados, saveData desligado).
  • navigator.keyboard.getLayoutMap() retorna um mapa US-QWERTY e, numa persona Windows, o AudioContext reporta a taxa de amostragem e a latência da pilha de áudio do Windows.
  • window.getScreenDetails() reporta um único monitor coerente; @media (pointer: fine) / (hover: hover) correspondem a um desktop com mouse.
  • URLs — sob uma persona Windows, new URL("C:/").protocol retorna file: e, a partir do 153 r26, todas as formas de uma página montar uma URL dão a mesma resposta. navigator.share / canShare ficam expostos para combinar com o UA do Windows.
  • WebGPU — as informações do adapter + limites/features de navigator.gpu acompanham a mesma GPU do WebGL (no build licenciado, restritas ao que o host consegue entregar).
  • Locale — Accept-Language, navigator.language, a lista completa de navigator.languages e Intl (thread principal + workers) vêm todos de uma única lista (acceptLanguage, ou geoip). O SDK define --accept-lang e --lang por você; usando os switches diretamente, passe os dois.
  • Síntese de voz — speechSynthesis serve o conjunto de vozes da persona, incluindo as vozes de rede do Google que um Chrome daquela versão lista (153 r27). fingerprintVoices: false mantém as vozes da própria máquina (152 r22).
  • Hardware — a leitura de velocidade do processador (navigator.cpuPerformance) segue a persona, e não o processador real (152 r20), e o teto de memória dos scripts (performance.memory.jsHeapSizeLimit) segue a memória que a persona declara (153 r26).
  • Codecs & dispositivos — MediaCapabilities.decodingInfo() reporta uma matriz de codecs da persona, e enumerateDevices(), um conjunto de dispositivos de mídia da persona (ids estáveis por seed, rótulos vazios antes da permissão).
  • UA-CH de alta entropia — bitness=64 / wow64=false / model; numa persona Linux, platformVersion vem vazio, como reporta o Chrome genuíno no Linux (153 r26). navigator.storage.estimate() reporta uma cota de disco realista quando você define storageQuota.

Experimental: canvas bridge com GPU real

Se quiser, encaminhe as operações de canvas / WebGL para um host remoto com GPU real, para que as leituras (getImageData / toDataURL / readPixels / measureText) sejam coerentes com a GPU que você apresenta — mesmo em hardware que não consegue renderizá-la localmente. Como o que se encaminha são as operações (e não imagens pré-gravadas), isso dá conta da maioria dos canvases, não só de probes conhecidos. Ative com --canvas-bridge-url=ws://host:port (mais --no-sandbox); sem ele, tudo roda localmente, exatamente como antes. O servidor de renderização controla um navegador no host com GPU real (um Chrome comum via CDP, ou um Clearcote headless). Veja o guia do canvas bridge para a configuração completa.

Overrides nativos de metadados & light_stealth

Junto com a persona derivada da seed, um conjunto de overrides nativos de valor único permite falsificar valores individuais de navigator/screen diretamente — hardwareConcurrency, deviceMemory, colorDepth, devicePixelRatio, maxTouchPoints e (opt-in) as dimensões de screen/avail*. Cada um é lido direto pelo seu getter, com a precedência flag > persona do --fingerprint > host real; assim, um valor fixo vence qualquer seed, e os overrides funcionam com ou sem uma seed de --fingerprint — eles nunca acionam o mecanismo de persona.

O SDK agrupa um subconjunto leve atrás de uma única flag, light_stealth: ela aplica um entre poucos pacotes de metadados coerentes (hardwareConcurrency, deviceMemory, colorDepth, devicePixelRatio, maxTouchPoints) usando apenas esses switches nativos. A seed só escolhe o pacote e não é repassada ao motor, então não há ruído de canvas nem de WebGL, e todas as sessões light_stealth numa mesma máquina compartilham o hash de canvas do host — use uma seed de fingerprint por conta, sem lightStealth, quando esses hashes precisarem ser diferentes. O ClientHello do TLS e a versão real do navegador não mudam. As dimensões de screen propositalmente não são falsificadas por padrão (opt-in via screenWidth etc.), porque uma tela falsa que não bate com a superfície de renderização real é fácil de perceber; num display sem escala, confira se o devicePixelRatio escolhido combina com ele, ou defina-o você mesmo. Uma opção explícita sempre vence o 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 os switches

Filtre por nome ou efeito, ou restrinja a um subsistema. Clique em um switch para ler o que ele faz e ver a sua forma válida mais curta.

Identidade e persona

Seed mestre (int ou string). Deriva a persona inteira (GPU, tela, hardware, fontes) e comanda o ruído de canvas e WebGL por site. Idioma e fuso horário não derivam do seed: vêm de --accept-lang / --timezone (SDK: acceptLanguage, timezone ou geoip). O áudio renderizado e os client rects não são perturbados; os escalares sampleRate/baseLatency/outputLatency do AudioContext vêm da persona quando há uma ativa — veja a documentação de fingerprint. Mesmo seed ⇒ mesma identidade.

Hardware e tela
GPU e WebGPU
Rede e locale
Ruído
Canvas bridge

Exemplos

Estes são conjuntos de flags de linha de comando puros. Para fluxos completos em Python e Node, veja Exemplos.

Uma identidade Windows completa e coerente:

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

Uma identidade Windows com strings de GPU personalizadas, no formato em que o ANGLE as reporta no 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)"

As strings mudam o que é reportado, não o que desenha: os pixels continuam vindo da GPU desta máquina. Indique a GPU em que você realmente renderiza, ou use o canvas bridge. A plataforma macos muda as strings de identidade (UA, client hints, navigator.platform), mas não há um modelo de máquina macOS por trás delas, e android é uma persona mobile de melhor esforço.

Fixe uma localização junto com o fuso horário dela, para que a geolocalização e o relógio concordem:

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

Importe os valores de uma máquina real

Em vez de uma seed sintética, você pode reportar os valores exatos que um Chrome real reportou — strings de GPU + tabela de getParameter, tela, fontes, vozes, áudio. Os valores são substituídos; a renderização continua sendo feita pela sua própria máquina, então um perfil não dá canvases diferentes a duas contas — uma seed de --fingerprint por conta, sim. Pegue um perfil na biblioteca curada clearcote-profiles (milhares de perfis de máquinas reais, marcados por fabricante de GPU) ou capture o seu com o coletor, depois carregue-o e comprove que ele foi carregado:

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.

Fluxo de captura: abra a página do coletor num Chrome real que você quer clonar, exporte o JSON e passe-o via fingerprintProfile (SDK) / --fingerprint-profile (motor). Verifique com tools/fingerprint-collect/verify_profile.py.

O que cada patch faz

Cada switch do build aberto corresponde a um patch legível do motor: os diffs ficam em patches/, com um resumo de uma linha de cada um em patches/README.md. Os switches marcados como “build licenciado” vêm do conjunto de patches próprio do build licenciado, que não é público.

A coerência importa mais do que qualquer valor isolado: mantenha plataforma, fuso horário, locale e GPU plausíveis em conjunto. Veja Arquitetura para entender como o motor os mantém consistentes, e Como a detecção funciona para saber por que isso é melhor do que falsificar valores via JavaScript.