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ície | Ruído por seed | Por 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) | Sim | A 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, toDataURL | Sim | Mesmo 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 AudioContext | Não — por design | A 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 texto | Não — por design | O 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 limites | Não | Segue 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:/").protocolretornafile: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.gpuacompanham a mesma GPU do WebGL (no build licenciado, restritas ao que o host consegue entregar). - Locale —
Accept-Language,navigator.language, a lista completa denavigator.languageseIntl(thread principal + workers) vêm todos de uma única lista (acceptLanguage, ougeoip). O SDK define--accept-lange--langpor você; usando os switches diretamente, passe os dois. - Síntese de voz —
speechSynthesisserve o conjunto de vozes da persona, incluindo as vozes de rede do Google que um Chrome daquela versão lista (153 r27).fingerprintVoices: falsemanté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, eenumerateDevices(), 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,platformVersionvem vazio, como reporta o Chrome genuíno no Linux (153 r26).navigator.storage.estimate()reporta uma cota de disco realista quando você definestorageQuota.
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.
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.
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.
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:
--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=8Uma identidade Windows com strings de GPU personalizadas, no formato em que o ANGLE as reporta no Windows:
--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:
--fingerprint=nyc-1 \
--timezone=America/New_York \
--fingerprint-location=40.7128,-74.0060Importe 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:
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.
Leituras relacionadas