4_ways_to_align_with_WebGL_fingerprinting_in_web_scraping

A impressão digital WebGL é uma técnica de identificação amplamente usada por sistemas anti-bot para detectar atividades automatizadas. Como utiliza sinais em nível de hardware, é bastante difícil de lidar, representando mais um desafio para quem depende de web scraping para fins comerciais. No entanto, há maneiras de manter um perfil discreto e coletar dados – continue lendo para saber mais.

O que é WebGL: uma explicação detalhada

WebGL significa Web Graphics Library, que é uma API JavaScript. Os navegadores a utilizam para exibir gráficos 2D e 3D dinâmicos sem exigir a instalação de plugins. Para realizar essas tarefas de renderização, a API depende fortemente da GPU, ou Graphics Processing Unit. Hoje em dia, os sites utilizam WebGL amplamente para efeitos visuais, jogos e interfaces gráficas complexas. Na verdade, WebGL 2 agora está ganhando espaço ativamente, pois oferece mais recursos. Navegadores modernos, como Chrome, Edge e Safari, têm 95-99% de suporte a WebGL 2, enquanto navegadores mais antigos e headless ainda não contam com esse suporte.

Como há centenas de GPUs disponíveis e até modelos do mesmo fabricante diferem drasticamente, a mesma imagem seria renderizada de maneiras diferentes. Além disso, o navegador, os drivers instalados e até variações de hardware, como a forma como a GPU e a placa-mãe estão conectadas, também influenciariam o resultado. É como visitar uma loja de tecnologia para casa e ver várias dezenas de TVs em exposição, todas exibindo o mesmo anúncio, mas com imagens ligeiramente diferentes. Isso se tornou a base da impressão digital WebGL. Observe que a impressão digital WebGL só acontece quando há JavaScript envolvido. Uma página HTML estática não consegue realizá-la.  

Impressão digital WebGL: uma visão geral passo a passo

De modo geral, todo o processo é assim:

  1. Um site cria um elemento <canvas>, que fica oculto no HTML da página. Ele inicializa o ambiente de renderização WebGL.
  2. JavaScript usa chamadas gl.getParameter() para extrair características da GPU, como fornecedor e modelo.
  3. Um script de impressão digital exige que um navegador renderize uma imagem de teste, e readPixels() é usado para extrair dados de pixels.

Você pode visitar BrowserLeaks, clicar em WebGL Report e ver sua impressão digital exclusiva. Ela se parece com isto:

Abaixo, você encontrará um exemplo de script usado por sites. Você também pode usá-lo para fins de teste, como diagnósticos, relatórios de bugs, testes de compatibilidade e muito mais. Lembre-se de executar o script em dispositivos que você possui ou estritamente após receber consentimento explícito. Evite usar o script para rastreamento ou desanonimização. Trate os resultados como dados sensíveis e não os compartilhe com terceiros, a menos que obtenha permissão. A DataImpulse não se responsabiliza pelo uso ilegal do script.

Etapa 1. Inicie o ambiente WebGL


// Lightweight FNV-1a hash for strings/byte arrays (synchronous, simple)
function fnv1aHashFromBytes(bytes) {
  let h = 0x811c9dc5 >>> 0;
  for (let i = 0; i < bytes.length; i++) {
    h ^= bytes[i];
    // multiply by FNV prime (mod 2^32)
    h = (h + ((h << 1) + (h << 4) + (h << 7) + (h << 8) + (h << 24))) >>> 0;
  }
  return ('00000000' + (h >>> 0).toString(16)).slice(-8);
}
function fnv1aHashFromString(s) {
  const bytes = new Uint8Array(s.length);
  for (let i = 0; i < s.length; i++) bytes[i] = s.charCodeAt(i) & 0xff;
  return fnv1aHashFromBytes(bytes);
}
// Create an (optionally hidden) canvas and return WebGLRenderingContext / WebGL2RenderingContext
function createWebGLContext(opts = {webgl2Preferred: true, preserveDrawingBuffer: false}) {
  const canvas = document.createElement('canvas');
  canvas.width = 256; canvas.height = 256;
  // optionally keep canvas off-DOM so it stays invisible
  canvas.style.display = 'none';
  document.body && document.body.appendChild(canvas); // append only if available
  let gl = null;
  if (opts.webgl2Preferred) {
    try { gl = canvas.getContext('webgl2', {preserveDrawingBuffer: opts.preserveDrawingBuffer}); } catch (e) {}
  }
  if (!gl) {
    try { gl = canvas.getContext('webgl', {preserveDrawingBuffer: opts.preserveDrawingBuffer}) || canvas.getContext('experimental-webgl'); } catch (e) {}
  }
  return {gl, canvas};
}

      
        
        
        
        
        
        
        
        
        
        
        

        
        
        
        
        
        
        
        
        
        

        
        
        
        
        
        
        
        
      

Este trecho cria uma tela invisível que roda em segundo plano enquanto você navega e define sua resolução. Além disso, se WebGL 2 não for compatível, ele recorrerá de forma adequada ao WebGL.

Etapa 2. Obtenha informações de hardware


function getWebGLHardwareInfo(gl) {
  const info = {};
  // Standard parameters
  info.maxTextureSize = gl.getParameter(gl.MAX_TEXTURE_SIZE);
  info.maxCombinedTextureImageUnits = gl.getParameter(gl.MAX_COMBINED_TEXTURE_IMAGE_UNITS);
  info.maxVertexAttribs = gl.getParameter(gl.MAX_VERTEX_ATTRIBS);
  info.maxVertexUniformVectors = gl.getParameter(gl.MAX_VERTEX_UNIFORM_VECTORS);
  info.maxFragmentUniformVectors = gl.getParameter(gl.MAX_FRAGMENT_UNIFORM_VECTORS);
  info.shaderPrecision = {
    vertexHighFloat: gl.getShaderPrecisionFormat(gl.VERTEX_SHADER, gl.HIGH_FLOAT),
    fragmentHighFloat: gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.HIGH_FLOAT)
  };
  // Try to get unmasked renderer/vendor (if browser exposes WEBGL_debug_renderer_info)
  const dbg = gl.getExtension('WEBGL_debug_renderer_info');
  if (dbg) {
    info.unmaskedVendor = gl.getParameter(dbg.UNMASKED_VENDOR_WEBGL);
    info.unmaskedRenderer = gl.getParameter(dbg.UNMASKED_RENDERER_WEBGL);
  } else {
    info.unmaskedVendor = null;
    info.unmaskedRenderer = null;
  }
  // Extensions list (useful fingerprinting signal)
  info.extensions = gl.getSupportedExtensions() || [];
  // Anisotropy (if available)
  const extAniso = gl.getExtension('EXT_texture_filter_anisotropic') ||
                   gl.getExtension('MOZ_EXT_texture_filter_anisotropic') ||
                   gl.getExtension('WEBKIT_EXT_texture_filter_anisotropic');
  if (extAniso) {
    const maxAniso = gl.getParameter(extAniso.MAX_TEXTURE_MAX_ANISOTROPY_EXT);
    info.maxAnisotropy = maxAniso;
  }
  return info;
}

      
        
        
        
        
        
        
        
        
        
        
        

        
        
        
        
        
        
        
        
        
        

        
        
        
        
        
        
        
        

        
      

Ele coleta vários detalhes, como fornecedor, modelo, tamanho da memória, tamanho máximo da textura, limites de uniformes de shader, extensão WebGL compatível e outros dados sobre sua GPU.

Etapa 3. Verifique as precisões dos shaders


function probeShaderPrecisions(gl) {
  const precisions = {};
  const shaderTypes = [
    {type: gl.VERTEX_SHADER, name: 'vertex'},
    {type: gl.FRAGMENT_SHADER, name: 'fragment'}
  ];
  const levels = [{p: gl.LOW_FLOAT, name: 'low'}, {p: gl.MEDIUM_FLOAT, name: 'medium'}, {p: gl.HIGH_FLOAT, name: 'high'}];
  for (const sh of shaderTypes) {
    precisions[sh.name] = {};
    for (const lv of levels) {
      const pf = gl.getShaderPrecisionFormat(sh.type, lv.p);
      precisions[sh.name][lv.name] = {rangeMin: pf.rangeMin, rangeMax: pf.rangeMax, precision: pf.precision};
    }
  }
  return precisions;
}

      
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
      

Os sites então perguntam à GPU quais níveis de precisão são compatíveis com operações de ponto flutuante e fragment shaders, e registram seus intervalos numéricos.

Agora que um script terminou de coletar informações de hardware e software, ele segue para a próxima etapa: renderização de imagem.

Etapa 4. Renderização da cena de teste


// Simple shader pair: position-only vertex shader and color-fragment that depends on coords
const VERT = `#version 300 es
in vec2 a_pos;
out vec2 v_uv;
void main() {
  v_uv = a_pos * 0.5 + 0.5;
  gl_Position = vec4(a_pos, 0.0, 1.0);
}`;
const FRAG = `#version 300 es
precision highp float;
in vec2 v_uv;
out vec4 outColor;
void main() {
  // deterministic function of coordinates that will exercise GPU math
  float r = sin(v_uv.x * 123.456) * 0.5 + 0.5;
  float g = cos(v_uv.y * 78.9) * 0.5 + 0.5;
  float b = fract((v_uv.x + v_uv.y) * 9876.54321);
  outColor = vec4(r, g, b, 1.0);
}`;

function createProgramWebGL2(gl, vertSrc, fragSrc) {
  function compile(src, type) {
    const s = gl.createShader(type);
    gl.shaderSource(s, src);
    gl.compileShader(s);
    if (!gl.getShaderParameter(s, gl.COMPILE_STATUS)) {
      const err = gl.getShaderInfoLog(s);
      gl.deleteShader(s);
      throw new Error('Shader compile error: ' + err);
    }
    return s;
  }
  const vs = compile(vertSrc, gl.VERTEX_SHADER);
  const fs = compile(fragSrc, gl.FRAGMENT_SHADER);
  const program = gl.createProgram();
  gl.attachShader(program, vs);
  gl.attachShader(program, fs);
  gl.linkProgram(program);
  if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
    const err = gl.getProgramInfoLog(program);
    throw new Error('Program link error: ' + err);
  }
  gl.deleteShader(vs);
  gl.deleteShader(fs);
  return program;
}

function renderTestAndHash(gl, canvas) {
  const isWebGL2 = typeof WebGL2RenderingContext !== 'undefined' && gl instanceof WebGL2RenderingContext;
  if (!isWebGL2) {
    // For WebGL1 we'd need slightly different shaders (no #version, use attribute/varying)
    // but the concept is the same. For clarity this example uses WebGL2 if available.
    console.warn('Example uses WebGL2 for deterministic shader pipeline; falling back may require shader edits.');
  }

  // Create program & buffer
  const prog = createProgramWebGL2(gl, VERT, FRAG);
  gl.useProgram(prog);
  const posLoc = gl.getAttribLocation(prog, 'a_pos');

  const quad = new Float32Array([
    -1, -1,
     1, -1,
    -1,  1,
     1,  1
  ]);
  const vb = gl.createBuffer();
  gl.bindBuffer(gl.ARRAY_BUFFER, vb);
  gl.bufferData(gl.ARRAY_BUFFER, quad, gl.STATIC_DRAW);
  gl.enableVertexAttribArray(posLoc);
  gl.vertexAttribPointer(posLoc, 2, gl.FLOAT, false, 0, 0);

  // Render to the canvas
  gl.viewport(0, 0, canvas.width, canvas.height);
  gl.clearColor(0,0,0,1);
  gl.clear(gl.COLOR_BUFFER_BIT);
  const t0 = performance.now();
  gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
  gl.finish(); // ensure rendering completes
  const t1 = performance.now();

  const renderTime = t1 - t0; // render timing in ms

  // Read pixels
  const px = new Uint8Array(canvas.width * canvas.height * 4);
  gl.readPixels(0, 0, canvas.width, canvas.height, gl.RGBA, gl.UNSIGNED_BYTE, px);

  const pixelHash = fnv1aHashFromBytes(px);

  // Clean up (optional)
  gl.deleteBuffer(vb);
  gl.deleteProgram(prog);

  return {pixelHash, renderTime};
}

      
        
        
        
        
        
        
        
        
        
        
        

        
        
        
        
        
        
        
        
        
        

        
        
        
        
        
        
        
        

        
        
        
        
        
        
        
        
        
        

        
        
        
        
        
        
        
        
        
        

        
        
        
        
        
        
        
        
        

        
        
        
        
        
        
        
        
        

        
        
        
        
        
        
        
        
        

        
        
        
        
        
        
        
        
        

        
      

O script solicita ao seu navegador que exiba uma cena 2D ou 3D que pode conter tons, gradientes ou efeitos específicos. Em seguida, lê os bytes dos pixels e obtém informações sobre as cores dos pixels, a textura da imagem, o sombreamento e os gradientes, calcula um hash desses valores e mede o tempo de renderização.

Etapa 5. Combinação de dados em uma impressão digital


function collectWebGLFingerprint(opts = {}) {
  const {gl, canvas} = createWebGLContext({webgl2Preferred: true, preserveDrawingBuffer: true});
  if (!gl) throw new Error('No WebGL available');

  const hw = getWebGLHardwareInfo(gl);
  const precisions = probeShaderPrecisions(gl);

  // optional: probe more capabilities
  const supportsFloatTextures = !!gl.getExtension('OES_texture_float') || !!gl.getExtension('EXT_color_buffer_float');

  // Render test and get pixel hash + timing
  const {pixelHash, renderTime} = renderTestAndHash(gl, canvas);

  // Combine into a fingerprint string
  const parts = [
    navigator.userAgent || '',
    hw.unmaskedVendor || '',
    hw.unmaskedRenderer || '',
    hw.maxTextureSize,
    hw.maxCombinedTextureImageUnits,
    hw.maxVertexAttribs,
    supportsFloatTextures ? 'floatTex' : '',
    hw.extensions ? hw.extensions.join(',') : '',
    JSON.stringify(precisions),
    pixelHash,
    String(Math.round(renderTime)) // include rounded render time
  ];
  const raw = parts.join('|');
  const fingerprint = fnv1aHashFromString(raw);

  // Return a readable object
  return {
    fingerprint,
    raw,
    components: {
      ua: navigator.userAgent,
      hardware: hw,
      precisions,
      pixelHash,
      renderTime
    }
  };
}

// Example usage:
try {
  const fp = collectWebGLFingerprint();
  console.log('WebGL fingerprint:', fp.fingerprint);
  console.log('Components:', fp.components);
} catch (e) {
  console.error('Could not collect WebGL fingerprint:', e);
}

      
        
        
        
        
        
        
        
        
        
        
        

        
        
        
        
        
        
        
        
        
        

        
        
        
        
        
        
        
        

        
        
        
        
        
        
        
        
        
        

        
        
        
        
        
        
        
        
        
        

Por fim, o script chama todas as etapas anteriores, combina todos os dados obtidos em uma única string longa e aplica hash novamente para criar um ID de fingerprint mais compacto e utilizável.

Todo esse processo ocorre em milissegundos e permanece invisível para você. Depois de coletar suas fingerprints, os sites as armazenam e as comparam com bancos de dados internos ou as enviam para serviços anti-bot para análise. Se uma fingerprint corresponder a um bot conhecido ou a um usuário suspeito, você corre o risco de receber um banimento.

Desafios do spoofing de WebGL

Há vários motivos pelos quais se alinhar às diretrizes de tráfego pode ser complicado.

  1. Diferenças no comportamento de renderização da GPU podem ser invisíveis aos olhos humanos, mas são óbvias para os sites e permanecem consistentes entre páginas. Limpar cookies ou usar o modo anônimo não ajuda.
  2. A renderização depende de dados vindos de hardware físico, que está fora do alcance de modificações em JavaScript e extensões de navegador.
  3. Você pode alterar os parâmetros de WebGL cada vez que carrega uma página; no entanto, se você afirmar que sua GPU é Intel, mas ela renderizar como NVIDIA, os sites detectarão a inconsistência, e isso é suficiente para acionar sistemas anti-bot.
  4. A maioria dos sistemas modernos de detecção identificará tentativas de adulterar a API WebGL.

Isso significa que não há como superar o fingerprinting de WebGL? Não, há várias técnicas que realmente funcionam. No entanto, vamos por partes.

O spoofing de fingerprint WebGL é legal?

Você tem o direito de proteger sua privacidade, portanto é errado dizer que fazer spoofing de uma fingerprint WebGL é ilegal. Isso também é usado para fins de teste. No entanto, depender de spoofing para cometer fraude ou violar os Termos de Serviço dos sites é contra a lei. Você deve sempre ler os ToS e evitar qualquer ação ilegal que possa prejudicar a privacidade de outras partes. As informações a seguir são fornecidas exclusivamente para fins educacionais. A DataImpulse não incentiva você a realizar nenhuma ação e não se responsabiliza por possíveis resultados.

Formas comprovadas de fazer spoofing de fingerprints WebGL

Ferramentas populares como Playwright ou Selenium, apoiadas por ferramentas como Playwright Stealth ou Undetected Chrome Driver, podem modificar ligeiramente parâmetros como tamanho da tela e outros. No entanto, é preciso ter cuidado para que as variáveis falsas e as imagens renderizadas correspondam entre si. Esse método não funcionará com projetos em larga escala ou ao tentar fazer scraping de sites usando o algoritmo de detecção de bots mais recente. O mesmo vale para extensões de navegador como WebGL Fingerprint Defender ou CanvasBlocker. Elas interceptam chamadas da API WebGL e retornam dados alterados, mas a imagem renderizada ainda pode não corresponder a eles.

  • Opte pela virtualização de GPU

Você pode fazer com que a GPU e/ou os drivers do navegador pareçam diferentes de uma GPU física real. Há várias abordagens que você pode adotar, por exemplo, substituir uma GPU real por um renderizador de software como Switch Shader ou usar máquinas virtuais com vários drivers de GPU. Por outro lado, se seu orçamento for limitado ou você não tiver habilidades técnicas sólidas, pense duas vezes antes de adotar esses métodos. Além disso, escalar seus projetos de scraping pode ser uma tarefa desafiadora.

  • Solicite APIs estáticas

Esse método funciona se você fizer scraping de dados públicos e o site de destino fornecer uma API pública ou oculta que retorna JSON. Em sites dinâmicos protegidos, isso não é uma opção, mas, em outros casos, é uma escolha digna de atenção, pois endpoints de API geralmente são menos protegidos.

  • Desative o WebGL

Desativar o WebGL elimina todos os principais sinais de fingerprinting de WebGL (vendor/renderer, extensões, precisão, hashes de pixels, tempos de renderização).

Como desativar o WebGL 

Chrome 

Método 1 – via Chrome Flags:

Na barra de endereços, digite chrome://flags, pressione Enter e selecione “Disable WebGL” ou –disable-webgl. Defina a flag como “Disabled”. Para ativar as alterações, reinicie o Chrome.

Método 2 – via linha de comando:

Clique com o botão direito no ícone do Google Chrome e vá para “Propriedades”. Procure a linha “Target”. Ela deve terminar com algo como chrome.exe. No final da linha, adicione um espaço e digite –disable-webgl. Clique em “Aplicar” >”OK”.

Firefox

Na barra de endereços, digite about:config e pressione Enter. Se aparecer um aviso, aceite-o. Procure por webgl.disable. Clique duas vezes e defina seu valor como true.

Safari

Vá para as preferências do Safari, abra a aba “Advanced” e selecione “Develop” (se disponível). Navegue até Experimental Features e encontre a opção de WebGL via Metal. Desmarque a caixa e reinicie o navegador.

Tenha cuidado, porém, pois muitos sites podem deixar de funcionar corretamente com o WebGL desativado.

Explorando vulnerabilidades do fingerprinting de WebGL

Você pode usar alguns pequenos truques. Por exemplo, scripts frequentemente armazenam resultados em cache e os reutilizam em vez de recalculá-los do zero. Você pode detectar isso e alterar seu ambiente antes que o cache seja reutilizado (para evitar que você seja associado a uma identidade registrada anteriormente) ou fazer com que um site recalcule um valor.

Detector:


// Helper: run the fingerprinting routine and return {time, result}
async function runFingerprintTrial(triggerFn) {
  const t0 = performance.now();
  const result = await triggerFn(); // should return fingerprint/hash if available
  const time = performance.now() - t0;
  return { time, result };
}

async function detectCachingRobust(triggerFingerprinting, trials = 5) {
  const runs = [];
  for (let i = 0; i < trials; i++) {
    // small delay to reduce micro-scheduling bias
    if (i > 0) await new Promise(r => setTimeout(r, 30));
    runs.push(await runFingerprintTrial(triggerFingerprinting));
  }

  // compute median time
  const times = runs.map(r => r.time).sort((a,b) => a-b);
  const medianTime = times[Math.floor(times.length/2)];

  // compare first vs median of later runs
  const firstTime = runs[0].time;
  const laterMedian = times.slice(1).length ? times.slice(1)[Math.floor((times.length-1)/2)] : medianTime;

  // check result stability (if trigger returns a fingerprint/hash)
  const results = runs.map(r => r.result);
  const allSame = results.every(x => x === results[0]);

  // simple heuristic: if first run is much slower *and* results identical, likely cached
  const speedRatio = firstTime / (laterMedian || 1);
  const likelyCached = (speedRatio > 5) && allSame;

  return {
    runs,
    medianTime,
    firstTime,
    laterMedian,
    speedRatio,
    results,
    allSame,
    likelyCached
  };
}

      
        
        
        
        
        
        
        
        
        
        
        

        
        
        
        
        
        
        
        
        
        

        
        
        
        
        
        
        
        

        
        
        
        
        

        
        
        
        
        
      

Force um ambiente novo:


function newWebGLContext(w=256,h=256) {
  const c = document.createElement('canvas');
  c.width = w; c.height = h;
  const gl = c.getContext('webgl2') || c.getContext('webgl') || c.getContext('experimental-webgl');
  return {c, gl};
}

      
        
        
        
        
        
        
      

Forçar recálculo:


localStorage.removeItem('fp_token'); // only if you can identify it
// or flush likely storage (be careful: may break site)
localStorage.clear();
indexedDB.deleteDatabase('site-db-name'); // needs precise names

      
        
        
        
        
      

Outra abordagem é detectar quando uma página adiciona um script de fingerprinting e executar um script de spoofing antes disso. No entanto, esteja ciente de que isso é facilmente detectável e pode interromper o comportamento da página; esse método é mais adequado para fins de teste ou defesa. 


// Robust script interception (best run at document_start)
(function() {
  if (window.__scriptInterceptorInstalled) return;
  window.__scriptInterceptorInstalled = true;

  // Your spoofing routine — define this to patch WebGL or other APIs.
  function injectSpoofing() {
    try {
      // Example: block debug info (replace with your real routine)
      const origGetExt = WebGLRenderingContext.prototype.getExtension;
      WebGLRenderingContext.prototype.getExtension = function(name) {
        if (name === 'WEBGL_debug_renderer_info') return null;
        return origGetExt.call(this, name);
      };
      // mark that spoofing ran
      window.__spoofingInjected = true;
    } catch (e) {
      // swallow errors — don't break page
      console.error('injectSpoofing error', e);
    }
  }

  // Helper: decide whether a script looks like fingerprinting (tweak to match)
  function looksLikeFingerprintScript(srcOrContent) {
    if (!srcOrContent) return false;
    const s = String(srcOrContent).toLowerCase();
    return s.includes('fingerprint') || s.includes('fingerprinting') || s.includes('fingerprintjs') || s.includes('fingerprint2');
  }

  // 1) Hook HTMLScriptElement.prototype.src property (setter)
  try {
    const proto = HTMLScriptElement.prototype;
    const srcDesc = Object.getOwnPropertyDescriptor(proto, 'src');
    if (srcDesc && srcDesc.configurable) {
      Object.defineProperty(proto, 'src', {
        configurable: true,
        enumerable: srcDesc.enumerable,
        get: function() { return srcDesc.get.call(this); },
        set: function(val) {
          try {
            // If the script's src looks suspicious, inject before setting
            if (looksLikeFingerprintScript(val) && !window.__spoofingInjected) {
              injectSpoofing();
            }
          } catch (e) { /* ignore */ }
          return srcDesc.set.call(this, val);
        }
      });
    }
  } catch (e) {
    // non-fatal
  }

  // 2) Hook setAttribute on script elements (covers element.setAttribute('src', ...))
  try {
    const origSetAttr = HTMLScriptElement.prototype.setAttribute;
    HTMLScriptElement.prototype.setAttribute = function(name, value) {
      try {
        if ((name + '').toLowerCase() === 'src' && looksLikeFingerprintScript(value) && !window.__spoofingInjected) {
          injectSpoofing();
        }
      } catch (e) {}
      return origSetAttr.call(this, name, value);
    };
  } catch (e) {}

  // 3) As a fallback, monitor append/insert to catch scripts that already had src set
  try {
    const origAppend = Node.prototype.appendChild;
    Node.prototype.appendChild = function(node) {
      try {
        if (node && node.tagName && node.tagName.toLowerCase() === 'script') {
          const src = node.src || node.getAttribute && node.getAttribute('src');
          if (looksLikeFingerprintScript(src) && !window.__spoofingInjected) {
            injectSpoofing();
          }
        }
      } catch (e) {}
      return origAppend.call(this, node);
    };

    const origInsertBefore = Node.prototype.insertBefore;
    Node.prototype.insertBefore = function(node, ref) {
      try {
        if (node && node.tagName && node.tagName.toLowerCase() === 'script') {
          const src = node.src || node.getAttribute && node.getAttribute('src');
          if (looksLikeFingerprintScript(src) && !window.__spoofingInjected) {
            injectSpoofing();
          }
        }
      } catch (e) {}
      return origInsertBefore.call(this, node, ref);
    };
  } catch (e) {}

  // 4) Small DOM observer as an extra safety net (lightweight)
  try {
    const obs = new MutationObserver((mutations) => {
      for (const m of mutations) {
        for (const n of m.addedNodes || []) {
          try {
            if (n && n.tagName && n.tagName.toLowerCase() === 'script') {
              const src = n.src || (n.getAttribute && n.getAttribute('src')) || n.textContent;
              if (looksLikeFingerprintScript(src) && !window.__spoofingInjected) {
                injectSpoofing();
                obs.disconnect();
                return;
              }
            }
          } catch (e) {}
        }
      }
    });
    obs.observe(document, { childList: true, subtree: true });
  } catch (e) {}

  // If the page already has script tags at start, scan them now
  try {
    const scripts = document.getElementsByTagName('script');
    for (let i = 0; i < scripts.length; i++) {
      const s = scripts[i];
      const src = s.src || s.getAttribute && s.getAttribute('src') || s.textContent;
      if (looksLikeFingerprintScript(src) && !window.__spoofingInjected) {
        injectSpoofing();
        break;
      }
    }
  } catch (e) {}
})();

      
        
        
        
        
        
        
        
        
        
        
        

        
        
        
        
        
        
        
        
        
        

        
        
        
        
        
        
        
        

        
        
        
        
        
        
        
        
        
        

        
        
        
        
        
        
        
        
        
        

        
        
        
        
        
        
        
        
        
        

        
        
        
        
        
        
        
        
        
        

        
        
        
        
        
        
        
        
        
        

        
        
        
        
        
        
        
        
        

        
        
        
        
        
        
        
        
        

        
        
        
        
        
        
        
        
        

        
        
        
        
        
        
        
        
        

        
        
      

Além disso, ambientes WebGL de páginas diferentes não conseguem se ver. Você pode criar contextos isolados para impedir que sites correspondam suas impressões digitais entre páginas.

WebGL vs Canvas

A impressão digital WebGL e a impressão digital Canvas costumam ser usadas de forma intercambiável, e é verdade que elas estão intimamente relacionadas. No entanto, elas não são 100% iguais.

A impressão digital Canvas analisa como os navegadores renderizam gráficos 2D usando HTML5 Canvas. Os resultados da renderização dependem principalmente da pilha gráfica do navegador e do mecanismo de renderização. Como resultado, é mais fácil de falsificar.

A impressão digital WebGL analisa como um navegador renderiza gráficos 2D e 3D. Os resultados de exibição dependem das características do hardware, tornando esse método mais informativo e difícil de contornar.

Resumo

Agora que você conhece os desafios de falsificação da impressão digital WebGL, talvez pense que estava melhor dez minutos atrás, quando ainda não sabia de nada. No entanto, seu objetivo não é ser invisível – você só precisa passar a impressão de um “usuário comum”. Para isso, sua impressão digital precisa parecer realista, ser consistente dentro de uma sessão e mudar de uma sessão para outra. E isso é totalmente possível. Por outro lado, o web scraping bem-sucedido só é possível quando você preserva a confidencialidade em todos os níveis e usa navegadores multi-login, soluções de falsificação de TLS, proxies, etc. Todas essas ferramentas e abordagens se complementam, permitindo que você permaneça fora do radar.  A DataImpulse tem exatamente o que você precisa para sua privacidade e anonimato – proxies obtidos legalmente e em whitelist para necessidades universais de scraping, além de um serviço de suporte humano 24/7 para que você possa coletar dados sem interrupção. Clique em “Experimente agora” ou entre em contato conosco em [email protected] para começar.

Share article: