Cum funcționează de fapt detectarea Puppeteer: 12 semnale care dau botii de gol
Framework-urile de automatizare moștenesc amprenta unui browser real, dar o modifică în zeci de moduri observabile. Un ghid al celor 12 semnale JavaScript, de rețea și comportamentale folosite pentru a prinde Puppeteer și Playwright.
Framework-urile de automatizare controlează browsere reale, ceea ce înseamnă că moștenesc amprenta browserului. Un Chrome condus de Puppeteer are același user agent, aceeași versiune de Chrome și același build Chromium ca un Chrome uman. Superficial, arată identic.
Dar framework-urile de automatizare modifică browserul în zeci de moduri subtile — majoritatea observabile din JavaScript. Detectarea nu e magie. E o listă de verificare.
1. Flag-ul navigator.webdriver
Cel mai simplu și cel mai cunoscut semnal. Când Chrome rulează sub un framework de automatizare, navigator.webdriver returnează true. Face parte din specificația W3C WebDriver, iar Chromium îl implementează în mod implicit.
Orice proiect serios de automatizare corectează acest lucru în câteva secunde. Puppeteer-extra-stealth suprascrie getter-ul. Utilizatorii Playwright injectează scripturi Object.defineProperty pe documentele noi. Flag-ul este trivial de ascuns.
Dar faptul că poate fi ascuns trivial este în sine un semnal. Un Chrome legitim nu are nevoie să ascundă nimic. Când o pagină verifică Object.getOwnPropertyDescriptor(Navigator.prototype, 'webdriver') și descriptorul arată diferit față de cel al unui browser nativ — automatizarea este prinsă chiar și după ce flag-ul a fost corectat.
2. Anomalia obiectului chrome
Chrome-ul legitim expune un obiect global window.chrome cu o structură substanțială — chrome.runtime, chrome.loadTimes, chrome.csi. Chrome-ul headless și configurările Puppeteer mai vechi fie omit complet acest obiect, fie expun o versiune redusă.
Pluginurile stealth recreează obiectul, dar recrearea este imperfectă. chrome.runtime.onConnect ar putea fi prezent fără chrome.runtime.PlatformOs. Semnăturile funcțiilor ar putea returna obiecte în loc de undefined. Fiecare nepotrivire este un semnal pozitiv.
3. Inconsistențe ale Permissions API
Browserele reale returnează rezultate coerente atunci când sunt interogate prin Permissions API. navigator.permissions.query({name: 'notifications'}) ar trebui să returneze 'default' dacă utilizatorul nu a acordat sau refuzat explicit permisiunea.
Chrome-ul headless returnează 'denied' în mod implicit, pentru că nu există o interfață care să afișeze o solicitare de permisiune. Framework-urile de automatizare corectează acest lucru — dar deseori incorect. Un indiciu frecvent: interogarea unei permisiuni neobișnuite precum 'clipboard-read' returnează un rezultat care nu se potrivește cu ce face Chrome-ul real în versiunea curentă.
4. Array-urile de pluginuri și tipuri MIME
navigator.plugins într-un browser real returnează un PluginArray cu intrări precum vizualizatorul PDF și pluginul PDF Chromium. În modul headless, acest array este gol.
Pluginurile stealth adaugă intrări false, dar acestea au frecvent proprietăți greșite — length lipsă, câmpuri de descriere incorecte sau obiecte plugin care nu se comportă ca instanțe Plugin atunci când sunt testate cu instanceof.
5. Nepotriviri de limbă și locale
navigator.language și navigator.languages ar trebui să se potrivească cu antetul HTTP Accept-Language. Ar trebui, de asemenea, să fie coerente cu fusul orar raportat de Intl.DateTimeFormat().resolvedOptions().timeZone.
Un bot care pretinde că se află la Kiev (fusul orar Europe/Kyiv), dar trimite Accept-Language: en-US,en;q=0.9 și raportează navigator.language === 'en-US' este posibil, dar statistic neobișnuit. Combinat cu un IP dintr-un datacenter din Frankfurt, tabloul devine clar.
6. Șirurile WebGL renderer
WebGL2RenderingContext.getParameter(WebGLDebugRendererInfo.UNMASKED_RENDERER_WEBGL) returnează producătorul și modelul GPU-ului. Pe o mașină reală, acesta ar putea fi:
ANGLE (Intel, Intel(R) UHD Graphics 620 Direct3D11 vs_5_0 ps_5_0)
Chrome-ul headless care rulează într-un container returnează adesea:
ANGLE (Google, Vulkan 1.3.0 (SwiftShader Device (Subzero)), SwiftShader driver)
Orice șir GPU care conține SwiftShader sau Subzero este un semnal puternic de bot.
7. Anomalii de ecran și viewport
O fereastră de browser reală are window.outerWidth și window.outerHeight care iau în calcul elementele de interfață ale browserului — bara de adrese, taburile, bara de marcaje. Diferența dintre dimensiunile exterioare și cele interioare este de obicei de 80–140 de pixeli pe verticală.
Browserele headless au adesea outerHeight === innerHeight pentru că nu există elemente de interfață de afișat. Puppeteer cu headless: false remediază acest lucru, dar multe configurări de automatizare încă rulează cu dimensiuni egale.
8. Enumerarea fonturilor
Utilizatorii reali au sute de fonturi instalate, care variază în funcție de sistemul de operare, limbă și aplicațiile instalate. Windows 11 cu Office instalat are peste 500 de fonturi. Containerele de automatizare care rulează Alpine Linux au 30.
Enumerarea fonturilor prin document.fonts.check() sau prin randare pe un canvas offscreen dezvăluie acest lucru imediat. Un browser care se raportează ca Windows 11 Chrome, dar expune doar 30 de fonturi, este aproape sigur automatizare.
9. Entropia mișcării mouse-ului
Utilizatorii reali mișcă mouse-ul cu tremur. Curbele sunt neliniare. Viteza variază. Între două click-uri există de obicei sute de evenimente mousemove.
Framework-urile de automatizare sintetizează mișcarea — fie cu linii drepte către coordonate, fie cu curbe programate cărora le lipsesc microtremururile unei mâini umane. Chiar și simulările cu curbe Bézier tind să aibă profiluri de accelerație suspect de netede.
Sistemele de detectare a boților colectează urmele de mișcare și le punctează în raport cu un model al mișcării umane. Urmele cu o viteză prea constantă, curbe prea curate sau microevenimente lipsă sunt marcate.
10. Semnături de sincronizare
Framework-urile de automatizare execută JavaScript cu caracteristici de sincronizare diferite față de browserele conduse de utilizatori. Sincronizarea performance.now() între evenimente, în special între pointerdown și pointerup, urmează o distribuție diferită.
Timpii reali de menținere a click-ului sunt de 50–150 ms, cu variație mare. Menținerea implicită a click-ului în Puppeteer este o valoare fixă — adesea 30 ms sau 100 ms — cu aproape nicio variație. O sută de click-uri cu timpi de menținere identici înseamnă automatizare fără dubiu.
11. Nepotrivirea amprentei TLS
Aceasta ocolește complet JavaScript-ul. Când Puppeteer se conectează, folosește stiva TLS a Chromium — la fel ca un Chrome real. Dar scraperele bazate pe undici sau axios care se pretind Chrome prin falsificarea user agent-ului folosesc stiva TLS a Node.js, care are o semnătură Client Hello vizibil diferită (hash JA4).
O cerere care pretinde User-Agent: Chrome/124.0.6367.60, dar prezintă o amprentă TLS Node.js, este confirmarea instantanee a unui client care nu este browser.
12. Scurgerea protocolului CDP
Puppeteer și Playwright comunică cu Chrome prin Chrome DevTools Protocol. În unele configurări, acest protocol lasă artefacte detectabile — proprietăți suplimentare pe obiectul Runtime, formate Error.stack modificate sau ieșiri specifice console.debug care apar doar când un client CDP este atașat.
Comanda Runtime.enable în special schimbă modul în care sunt randate stack trace-urile. Boții care nu declanșează niciodată erori evită acest semnal, dar orice flux de automatizare care întâmpină o excepție (și majoritatea o fac) lasă amprente.
Stratificarea acestor semnale
Niciun semnal izolat nu constituie dovada automatizării. Un utilizator real cu un driver GPU neobișnuit ar putea returna un șir WebGL ciudat. Un utilizator preocupat de confidențialitate ar putea avea navigator.plugins modificat.
Puterea detectării moderne vine din combinarea semnalelor — 12 indicii slabe sunt mai puternice decât un singur indiciu puternic, pentru că un utilizator real rareori declanșează mai mult de 2–3 anomalii simultan.
Sistemele de detectare atribuie fiecărui semnal o pondere și calculează un scor. Peste prag, vizitatorul este tratat ca automatizare. Pragul exact și ponderile sunt cele mai greu de nimerit — prea strict și utilizatorii legitimi sunt blocați; prea permisiv și boții sofisticați se strecoară.
Semnalele în sine sunt partea ușoară. Aproape fiecare framework de automatizare le scurge pe toate cele 12. Adevărata inginerie constă în a decide care combinații contează, care sunt fals pozitive și cum să actualizezi modelul pe măsură ce framework-urile de automatizare se adaptează.