Cara Kerja Deteksi Puppeteer yang Sebenarnya: 12 Sinyal yang Membongkar Bot
Framework automation mewarisi fingerprint browser asli tetapi mengubahnya dengan puluhan cara yang bisa diamati. Panduan lapangan untuk 12 sinyal JavaScript, jaringan, dan perilaku yang dipakai untuk menangkap Puppeteer dan Playwright.
Framework automation mengendalikan browser asli, yang berarti mereka mewarisi fingerprint browser tersebut. Chrome yang dijalankan Puppeteer memiliki user agent yang sama, versi Chrome yang sama, dan build Chromium yang sama seperti Chrome milik manusia. Secara sekilas, keduanya tampak identik.
Namun framework automation mengubah browser dengan puluhan cara halus — sebagian besar bisa diamati dari JavaScript. Deteksi bukanlah sihir. Ini adalah sebuah checklist.
1. Flag navigator.webdriver
Sinyal paling sederhana dan paling terkenal. Ketika Chrome berjalan di bawah framework automation, navigator.webdriver mengembalikan true. Ini bagian dari spesifikasi W3C WebDriver dan Chromium mengimplementasikannya secara default.
Setiap proyek automation yang serius menambal ini dalam hitungan detik. Puppeteer-extra-stealth menimpa getter-nya. Pengguna Playwright menyuntikkan skrip Object.defineProperty pada dokumen baru. Flag ini sepele untuk disembunyikan.
Tetapi fakta bahwa ia bisa disembunyikan dengan sepele justru merupakan sinyal tersendiri. Chrome yang sah tidak perlu menyembunyikan apa pun. Ketika sebuah halaman memeriksa Object.getOwnPropertyDescriptor(Navigator.prototype, 'webdriver') dan descriptor-nya terlihat berbeda dari browser native — automation tertangkap bahkan setelah flag ditambal.
2. Anomali objek chrome
Chrome yang sah mengekspos objek global window.chrome dengan struktur yang substansial — chrome.runtime, chrome.loadTimes, chrome.csi. Chrome headless dan setup Puppeteer lama entah menghilangkan objek ini sepenuhnya atau mengekspos versi yang dipangkas.
Plugin stealth membangun ulang objek tersebut, tetapi pembangunannya tidak sempurna. chrome.runtime.onConnect bisa ada tanpa chrome.runtime.PlatformOs. Signature fungsi bisa mengembalikan objek alih-alih undefined. Setiap ketidakcocokan adalah sinyal positif.
3. Inkonsistensi Permissions API
Browser asli mengembalikan hasil yang koheren ketika ditanya melalui Permissions API. navigator.permissions.query({name: 'notifications'}) seharusnya mengembalikan 'default' jika pengguna belum secara eksplisit memberikan atau menolak izin.
Chrome headless mengembalikan 'denied' secara default, karena tidak ada UI untuk menampilkan prompt izin. Framework automation menambal ini — tetapi sering kali secara keliru. Petunjuk umum: menanyakan izin tidak biasa seperti 'clipboard-read' mengembalikan hasil yang tidak cocok dengan yang dilakukan Chrome asli pada versi saat ini.
4. Array plugin dan tipe MIME
navigator.plugins pada browser asli mengembalikan PluginArray dengan entri seperti PDF viewer dan plugin PDF Chromium. Dalam mode headless, array ini kosong.
Plugin stealth menambahkan entri palsu, tetapi entri tersebut sering memiliki properti yang salah — length yang hilang, field description yang keliru, atau objek plugin yang tidak berperilaku seperti instance Plugin ketika diuji dengan instanceof.
5. Ketidakcocokan bahasa dan locale
navigator.language dan navigator.languages seharusnya cocok dengan header HTTP Accept-Language. Keduanya juga seharusnya konsisten dengan zona waktu yang dilaporkan oleh Intl.DateTimeFormat().resolvedOptions().timeZone.
Sebuah bot yang mengaku berada di Kyiv (zona waktu Europe/Kyiv) tetapi mengirim Accept-Language: en-US,en;q=0.9 dan melaporkan navigator.language === 'en-US' memang mungkin tetapi secara statistik tidak biasa. Ketika dikombinasikan dengan IP dari datacenter di Frankfurt, gambarannya menjadi jelas.
6. String WebGL renderer
WebGL2RenderingContext.getParameter(WebGLDebugRendererInfo.UNMASKED_RENDERER_WEBGL) mengembalikan vendor dan model GPU. Pada mesin asli, ini mungkin berupa:
ANGLE (Intel, Intel(R) UHD Graphics 620 Direct3D11 vs_5_0 ps_5_0)
Chrome headless yang berjalan dalam container sering mengembalikan:
ANGLE (Google, Vulkan 1.3.0 (SwiftShader Device (Subzero)), SwiftShader driver)
String GPU apa pun yang mengandung SwiftShader atau Subzero adalah sinyal bot yang kuat.
7. Anomali layar dan viewport
Jendela browser asli memiliki window.outerWidth dan window.outerHeight yang memperhitungkan chrome browser — address bar, tab, bookmark bar. Selisih antara dimensi luar dan dalam biasanya 80–140 piksel secara vertikal.
Browser headless sering memiliki outerHeight === innerHeight karena tidak ada chrome untuk ditampilkan. Puppeteer dengan headless: false memperbaiki ini, tetapi banyak setup automation masih berjalan dengan dimensi yang sama.
8. Enumerasi font
Pengguna asli memiliki ratusan font terpasang, bervariasi menurut OS, bahasa, dan aplikasi yang terpasang. Windows 11 dengan Office terpasang memiliki 500+ font. Container automation yang menjalankan Alpine Linux memiliki 30.
Enumerasi font melalui document.fonts.check() atau rendering offscreen canvas mengungkap ini seketika. Browser yang melaporkan dirinya sebagai Windows 11 Chrome tetapi hanya mengekspos 30 font hampir pasti adalah automation.
9. Entropi gerakan mouse
Pengguna asli menggerakkan mouse dengan jitter. Kurvanya non-linear. Kecepatannya bervariasi. Di antara dua klik, biasanya ada ratusan event mousemove.
Framework automation mensintesis gerakan — entah dengan garis lurus menuju koordinat, atau dengan kurva terprogram yang tidak memiliki mikrotremor tangan manusia. Bahkan simulasi kurva Bezier cenderung memiliki profil akselerasi yang mencurigakan mulusnya.
Sistem deteksi bot mengumpulkan jejak gerakan dan menilainya terhadap model gerakan manusia. Jejak dengan kecepatan yang terlalu konsisten, kurva yang terlalu bersih, atau microevent yang hilang akan ditandai.
10. Tanda tangan timing
Framework automation mengeksekusi JavaScript dengan karakteristik timing yang berbeda dari browser yang dikendalikan pengguna. Timing performance.now() antar event, terutama antara pointerdown dan pointerup, mengikuti distribusi yang berbeda.
Waktu tahan klik asli adalah 50–150ms dengan varians tinggi. Waktu tahan klik default Puppeteer adalah nilai tetap — sering 30ms atau 100ms — dengan hampir tanpa varians. Seratus klik dengan waktu tahan yang identik adalah automation yang pasti.
11. Ketidakcocokan fingerprint TLS
Yang satu ini melewati JavaScript sepenuhnya. Ketika Puppeteer terhubung, ia menggunakan stack TLS Chromium — sama seperti Chrome asli. Namun scraper berbasis undici atau axios yang berpura-pura menjadi Chrome melalui pemalsuan user agent menggunakan stack TLS Node.js, yang memiliki signature Client Hello (hash JA4) yang jelas berbeda.
Sebuah request yang mengaku User-Agent: Chrome/124.0.6367.60 tetapi menampilkan fingerprint TLS Node.js adalah konfirmasi instan bahwa klien tersebut bukan browser.
12. Kebocoran protokol CDP
Puppeteer dan Playwright berkomunikasi dengan Chrome melalui Chrome DevTools Protocol. Pada beberapa konfigurasi, protokol ini meninggalkan artefak yang bisa dideteksi — properti tambahan pada objek Runtime, format Error.stack yang berubah, atau output console.debug tertentu yang hanya muncul ketika klien CDP terpasang.
Perintah Runtime.enable khususnya mengubah cara stack trace dirender. Bot yang tidak pernah memicu error menghindari sinyal ini, tetapi alur automation apa pun yang mengenai exception (dan sebagian besar mengalaminya) meninggalkan fingerprint.
Melapiskan sinyal-sinyal ini
Tidak ada satu sinyal pun yang menjadi bukti automation. Pengguna asli dengan driver GPU yang tidak biasa mungkin mengembalikan string WebGL yang aneh. Pengguna yang mementingkan privasi mungkin telah memodifikasi navigator.plugins.
Kekuatan deteksi modern berasal dari menggabungkan sinyal — 12 sinyal positif lemah lebih kuat daripada satu sinyal positif kuat, karena pengguna asli jarang memicu lebih dari 2–3 anomali secara bersamaan.
Sistem deteksi memberikan bobot pada setiap sinyal dan menghitung skor. Di atas ambang batas, pengunjung diperlakukan sebagai automation. Ambang batas dan bobot yang tepat adalah bagian tersulit untuk dibuat benar — terlalu ketat dan pengguna sah terblokir; terlalu longgar dan bot canggih lolos.
Sinyal-sinyalnya sendiri adalah bagian yang mudah. Hampir setiap framework automation membocorkan ke-12-nya. Rekayasa yang sesungguhnya adalah memutuskan kombinasi mana yang penting, mana yang false positive, dan bagaimana memperbarui model saat framework automation beradaptasi.