4_ways_to_align_with_WebGL_fingerprinting_in_web_scraping

WebGL fingerprinting adalah teknik identifikasi yang banyak digunakan oleh sistem anti-bot untuk mendeteksi aktivitas otomatis. Karena memanfaatkan sinyal tingkat perangkat keras, teknik ini cukup sulit ditangani dan menjadi tantangan lain bagi pihak yang mengandalkan web scraping untuk keperluan bisnis. Namun, ada cara untuk tetap tidak mencolok dan mengumpulkan data. Baca selengkapnya untuk mengetahui lebih lanjut.

Apa itu WebGL: penjelasan terperinci

WebGL adalah singkatan dari Web Graphics Library, yaitu JavaSkrip API. Browser menggunakannya untuk menampilkan grafis 2D dan 3D dinamis tanpa perlu menginstal plugin. Untuk menjalankan tugas rendering ini, API sangat bergantung pada GPU, atau Graphics Processing Unit. Saat ini situs web banyak memanfaatkan WebGL untuk efek visual, game, dan antarmuka grafis yang kompleks. Bahkan, WebGL 2 kini semakin banyak digunakan karena menawarkan lebih banyak fitur. Browser modern, seperti Chrome, Edge, dan Safari, mendukung WebGL 2 sebesar 95-99%, sedangkan browser lama dan browser tanpa antarmuka masih belum mendukungnya.

Karena tersedia ratusan GPU dan bahkan model dari produsen yang sama dapat sangat berbeda, gambar yang sama akan dirender secara berbeda. Selain itu, browser, driver yang terinstal, serta variasi perangkat keras, seperti cara GPU dan motherboard terhubung, juga memengaruhi hasilnya. Bayangkan Anda mengunjungi toko elektronik rumah tangga dan melihat puluhan TV dipajang, semuanya menampilkan iklan yang sama, tetapi gambarnya sedikit berbeda. Inilah yang menjadi dasar WebGL fingerprinting. Perlu diingat, WebGL fingerprinting hanya terjadi saat JavaSkrip digunakan. Halaman HTML statis tidak dapat melakukannya.  

WebGL fingerprinting: gambaran langkah demi langkah

Secara garis besar, seluruh prosesnya seperti ini:

  1. Sebuah situs web membuat elemen <canvas> yang disembunyikan dalam HTML halaman. Elemen ini menginisialisasi lingkungan rendering WebGL.
  2. JavaSkrip menggunakan pemanggilan gl.getParameter() untuk mengambil karakteristik GPU seperti vendor dan model.
  3. Sebuah skrip fingerprinting meminta browser merender gambar uji, lalu readPixels() digunakan untuk mengambil data piksel.

Anda dapat mengunjungi BrowserLeaks, klik WebGL Report, dan lihat fingerprint unik Anda. Tampilannya seperti ini:

Di bawah ini, Anda akan menemukan contoh skrip yang digunakan situs web. Anda juga dapat menggunakannya untuk tujuan pengujian, seperti diagnostik, laporan bug, pengujian kompatibilitas, dan lainnya. Ingatlah untuk menjalankan skrip pada perangkat milik Anda atau hanya setelah memperoleh persetujuan eksplisit. Hindari penggunaan skrip untuk pelacakan atau pengungkapan anonimitas. Perlakukan hasilnya sebagai data sensitif dan jangan bagikan kepada pihak ketiga tanpa izin. DataImpulse tidak bertanggung jawab atas penggunaan skrip yang melanggar hukum.

Langkah 1. Jalankan lingkungan 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};
}

      
        
        
        
        
        
        
        
        
        
        
        

        
        
        
        
        
        
        
        
        
        

        
        
        
        
        
        
        
        
      

Bagian ini membuat canvas tak terlihat yang berjalan di latar belakang saat Anda menjelajah serta menetapkan resolusinya. Selain itu, jika WebGL 2 tidak didukung, skrip ini akan beralih dengan mulus ke WebGL.

Langkah 2. Dapatkan informasi perangkat keras


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

      
        
        
        
        
        
        
        
        
        
        
        

        
        
        
        
        
        
        
        
        
        

        
        
        
        
        
        
        
        

        
      

Skrip ini mengumpulkan berbagai detail seperti vendor, model, ukuran memori, ukuran tekstur maksimum, batas shader uniform, ekstensi WebGL yang didukung, dan data lain tentang GPU Anda.

Langkah 3. Periksa presisi shader


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

      
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
      

Situs web lalu menanyakan kepada GPU tingkat presisi yang didukung untuk operasi floating-point dan fragment shader, kemudian mencatat rentang numeriknya.

Setelah skrip selesai mengumpulkan informasi perangkat keras dan perangkat lunak, skrip tersebut melanjutkan ke langkah berikutnya: rendering gambar.

Langkah 4. Uji rendering adegan


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

      
        
        
        
        
        
        
        
        
        
        
        

        
        
        
        
        
        
        
        
        
        

        
        
        
        
        
        
        
        

        
        
        
        
        
        
        
        
        
        

        
        
        
        
        
        
        
        
        
        

        
        
        
        
        
        
        
        
        

        
        
        
        
        
        
        
        
        

        
        
        
        
        
        
        
        
        

        
        
        
        
        
        
        
        
        

        
      

Skrip meminta browser Anda menampilkan adegan 2D atau 3D yang dapat berisi corak, gradien, atau efek tertentu. Kemudian skrip membaca byte piksel dan memperoleh informasi tentang warna piksel, tekstur gambar, pencahayaan, serta gradien, menghitung hash dari nilai-nilai tersebut, dan mengukur waktu rendering.

Langkah 5. Menggabungkan data dalam fingerprint


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

      
        
        
        
        
        
        
        
        
        
        
        

        
        
        
        
        
        
        
        
        
        

        
        
        
        
        
        
        
        

        
        
        
        
        
        
        
        
        
        

        
        
        
        
        
        
        
        
        
        

Terakhir, skrip memanggil semua langkah sebelumnya, menggabungkan seluruh data yang diperoleh menjadi satu string panjang, lalu menghitung hash-nya kembali untuk membuat ID fingerprint yang lebih ringkas dan dapat digunakan.

Seluruh proses ini berlangsung dalam hitungan milidetik dan tidak terlihat oleh Anda. Setelah mengumpulkan fingerprint Anda, situs web menyimpannya lalu membandingkannya dengan database internal atau mengirimkannya ke layanan anti-bot untuk dianalisis. Jika fingerprint cocok dengan bot yang dikenal atau pengguna mencurigakan, Anda berisiko terkena pemblokiran.

Tantangan WebGL spoofing

Ada beberapa alasan mengapa menyelaraskan diri dengan pedoman lalu lintas bisa menjadi rumit.

  1. Perbedaan perilaku rendering GPU mungkin tidak terlihat oleh mata manusia, tetapi jelas bagi situs web dan tetap konsisten di berbagai halaman. Menghapus cookie atau menggunakan mode incognito tidak membantu.
  2. Rendering bergantung pada data yang berasal dari perangkat keras fisik, yang tidak dapat dijangkau oleh perubahan JavaSkrip maupun ekstensi browser.
  3. Anda dapat mengubah parameter WebGL setiap kali memuat halaman. Namun, jika Anda menyatakan GPU Anda adalah Intel tetapi hasil rendernya seperti NVIDIA, situs web akan mendeteksi ketidakkonsistenan tersebut, dan itu sudah cukup untuk memicu sistem anti-bot.
  4. Sebagian besar sistem deteksi modern akan menangkap upaya untuk memanipulasi WebGL API.

Apakah ini berarti tidak ada cara untuk melewati WebGL fingerprinting? Tidak, ada beberapa teknik yang benar-benar efektif. Namun, mari mulai dari hal yang paling mendasar.

Apakah WebGL fingerprint spoofing legal?

Anda berhak melindungi privasi, jadi keliru jika dikatakan bahwa spoofing WebGL fingerprint itu ilegal. Teknik ini juga digunakan untuk tujuan pengujian. Namun, mengandalkan spoofing untuk melakukan penipuan atau melanggar Ketentuan Layanan situs web adalah tindakan melanggar hukum. Anda harus selalu membaca ToS dan menghindari tindakan ilegal yang dapat merugikan privasi pihak lain. Informasi selanjutnya disediakan semata-mata untuk tujuan edukasi. DataImpulse tidak mendorong Anda untuk melakukan tindakan apa pun dan tidak bertanggung jawab atas kemungkinan akibatnya.

Cara yang terbukti efektif untuk melakukan spoofing WebGL fingerprint

Alat populer seperti Playwright atau Selenium, yang didukung alat seperti Playwright Stealth atau Undetected Chrome Driver, dapat sedikit mengubah parameter seperti ukuran layar dan lainnya. Namun, Anda perlu berhati-hati agar variabel palsu dan gambar yang dirender saling cocok. Metode ini tidak akan berhasil untuk proyek berskala besar atau saat mencoba melakukan scraping pada situs web yang menggunakan algoritma deteksi bot terbaru. Hal yang sama berlaku untuk ekstensi browser seperti WebGL Fingerprint Defender atau CanvasBlocker. Ekstensi tersebut mencegat pemanggilan WebGL API dan mengembalikan data yang diubah, tetapi gambar yang dirender tetap dapat tidak cocok dengan data itu.

  • Pilih virtualisasi GPU

Anda dapat membuat GPU dan/atau driver browser terlihat berbeda dari GPU fisik yang sebenarnya. Ada beberapa pendekatan yang dapat digunakan, misalnya mengganti GPU asli dengan perender perangkat lunak seperti Switch Shader atau menggunakan mesin virtual dengan berbagai driver GPU. Namun, jika anggaran Anda terbatas atau kemampuan teknis Anda belum kuat, pikirkan matang-matang sebelum menerapkan metode tersebut. Selain itu, memperluas skala proyek scraping Anda dapat menjadi tugas yang menantang.

  • Minta API statis

Metode ini berhasil jika Anda melakukan scraping data publik dan situs web target menyediakan API publik atau tersembunyi yang mengembalikan JSON. Pada situs web dinamis yang terlindungi, ini bukan pilihan, tetapi dalam kasus lain, metode ini layak dipertimbangkan karena endpoint API umumnya kurang terlindungi.

  • Nonaktifkan WebGL

Menonaktifkan WebGL meniadakan semua sinyal utama WebGL fingerprinting (vendor/renderer, ekstensi, presisi, hash piksel, waktu rendering).

Cara menonaktifkan WebGL 

Chrome 

Metode 1 – melalui flag Chrome:

Di bilah alamat, ketik chrome://flags, tekan Enter, lalu pilih “Nonaktifkan WebGL” atau –disable-webgl. Atur flag ke “Dinonaktifkan”. Untuk mengaktifkan perubahan, jalankan ulang Chrome.

Metode 2 – melalui command line:

Klik kanan ikon Google Chrome dan buka “Properti”. Cari baris “Target” . Baris tersebut seharusnya berakhir dengan teks seperti chrome.exe. Di bagian paling akhir baris, tambahkan spasi, lalu ketik –disable-webgl. Klik “Apply” >”OK”.

Firefox

Di bilah alamat, ketik about:config dan tekan Enter. Jika peringatan muncul, setujui. Cari webgl.disable. Klik dua kali dan atur nilainya menjadi true.

Safari

Buka preferensi Safari, buka tab “Lanjutan”, lalu pilih “Develop” (jika tersedia). Buka Fitur Eksperimental dan cari opsi WebGL melalui Metal. Hapus centang pada kotak dan mulai ulang browser.

Namun, berhati-hatilah karena banyak situs web mungkin tidak berfungsi dengan benar jika WebGL dinonaktifkan.

Memanfaatkan kerentanan WebGL fingerprinting

Anda dapat menggunakan beberapa trik kecil. Misalnya, skrip sering menyimpan hasil dalam cache dan menggunakannya kembali alih-alih menghitung ulang dari awal. Anda dapat mendeteksinya lalu mengubah lingkungan Anda sebelum cache digunakan kembali (untuk mencegah pencocokan dengan identitas yang telah tercatat sebelumnya) atau membuat situs menghitung ulang suatu nilai.

Pendeteksi:


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

      
        
        
        
        
        
        
        
        
        
        
        

        
        
        
        
        
        
        
        
        
        

        
        
        
        
        
        
        
        

        
        
        
        
        

        
        
        
        
        
      

Paksa lingkungan baru:


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

      
        
        
        
        
        
        
      

Paksa penghitungan ulang:


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

      
        
        
        
        
      

Pendekatan lain adalah mendeteksi saat sebuah halaman menambahkan skrip fingerprinting dan menjalankan skrip spoofing terlebih dahulu. Namun, perlu diingat bahwa cara ini mudah dideteksi dan dapat mengganggu perilaku halaman. Metode ini lebih cocok untuk tujuan pengujian atau defensif. 


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

      
        
        
        
        
        
        
        
        
        
        
        

        
        
        
        
        
        
        
        
        
        

        
        
        
        
        
        
        
        

        
        
        
        
        
        
        
        
        
        

        
        
        
        
        
        
        
        
        
        

        
        
        
        
        
        
        
        
        
        

        
        
        
        
        
        
        
        
        
        

        
        
        
        
        
        
        
        
        
        

        
        
        
        
        
        
        
        
        

        
        
        
        
        
        
        
        
        

        
        
        
        
        
        
        
        
        

        
        
        
        
        
        
        
        
        

        
        
      

Selain itu, lingkungan WebGL dari halaman yang berbeda tidak dapat saling melihat. Anda dapat membuat konteks terisolasi untuk mencegah situs web mencocokkan fingerprint Anda di berbagai halaman.

WebGL vs Canvas

WebGL fingerprinting dan Canvas fingerprinting sering digunakan secara bergantian, dan keduanya memang berkaitan erat. Namun, keduanya tidak 100% sama.

Canvas fingerprinting menganalisis cara browser merender grafis 2D menggunakan HTML5 Canvas. Hasil rendering terutama bergantung pada tumpukan grafis browser dan mesin rendering. Karena itu, metode ini lebih mudah dipalsukan.

WebGL fingerprinting menganalisis cara browser merender grafis 2D dan 3D. Hasil tampilan bergantung pada karakteristik perangkat keras, sehingga metode ini lebih informatif dan lebih sulit dilewati.

Ringkasan

Kini setelah memahami tantangan WebGL fingerprinting spoofing, mungkin Anda merasa keadaan Anda lebih baik sepuluh menit lalu, saat belum mengetahui apa-apa. Namun, tujuan Anda bukan menjadi tidak terlihat. Anda hanya perlu memberikan kesan sebagai “pengguna biasa”. Untuk itu, fingerprint Anda harus terlihat realistis, konsisten dalam satu session, dan berubah dari satu session ke session berikutnya. Hal itu sepenuhnya dapat dicapai. Di sisi lain, web scraping yang berhasil hanya mungkin dilakukan jika Anda menjaga kerahasiaan di semua tingkat dan menggunakan browser multi-login, solusi TLS spoofing, proxy, dan sebagainya. Semua alat dan pendekatan tersebut saling memperkuat sehingga Anda dapat tetap tidak mencolok. DataImpulse menyediakan solusi untuk privasi dan anonimitas Anda: proxy yang diperoleh secara legal dan masuk whitelist untuk kebutuhan scraping universal, disertai layanan dukungan manusia 24/7 agar Anda dapat mengumpulkan data tanpa gangguan. Klik “Coba sekarang” atau hubungi kami di [email protected] untuk memulai.

Share article: