איך זיהוי Puppeteer באמת עובד: 12 סימנים שמסגירים בוטים
מסגרות אוטומציה יורשות את טביעת האצבע של דפדפן אמיתי אך משנות אותה בעשרות דרכים נצפות. מדריך שטח ל-12 הסימנים ב-JavaScript, ברשת ובהתנהגות שבהם מגנים תופסים Puppeteer ו-Playwright.
מסגרות אוטומציה שולטות בדפדפנים אמיתיים, ומכאן שהן יורשות את טביעת האצבע של הדפדפן. ל-Chrome מונע-Puppeteer יש אותו user agent, אותה גרסת Chrome ואותו בילד Chromium כמו ל-Chrome של אדם. באופן שטחי הם נראים זהים.
אך מסגרות אוטומציה משנות את הדפדפן בעשרות דרכים דקות — רובן נצפות מ-JavaScript. זיהוי אינו קסם. זו רשימת בדיקה.
1. הדגל navigator.webdriver
הסימן הפשוט והמפורסם ביותר. כאשר Chrome רץ תחת מסגרת אוטומציה, navigator.webdriver מחזיר true. זה חלק ממפרט W3C WebDriver, ו-Chromium מממש אותו כברירת מחדל.
כל פרויקט אוטומציה רציני מטליא את זה בתוך שניות. Puppeteer-extra-stealth דורס את ה-getter. משתמשי Playwright מזריקים סקריפטים של Object.defineProperty על מסמכים חדשים. הסתרת הדגל טריוויאלית.
אבל עצם העובדה שניתן להסתירו בטריוויאליות היא סימן בפני עצמה. Chrome לגיטימי לא צריך להסתיר דבר. כאשר עמוד בודק את Object.getOwnPropertyDescriptor(Navigator.prototype, 'webdriver') וה-descriptor נראה שונה מזה של דפדפן מקורי — האוטומציה נתפסת גם אחרי שהדגל הוטלא.
2. חריגת אובייקט chrome
Chrome לגיטימי חושף אובייקט גלובלי window.chrome עם מבנה מהותי — chrome.runtime, chrome.loadTimes, chrome.csi. גרסאות Chrome headless ו-Puppeteer ישנות יותר או משמיטות את האובייקט הזה כליל, או חושפות גרסה מקוצצת.
תוספי stealth משחזרים את האובייקט, אך השחזור אינו מושלם. ייתכן ש-chrome.runtime.onConnect יהיה נוכח בלי chrome.runtime.PlatformOs. חתימות פונקציה עשויות להחזיר אובייקטים במקום undefined. כל אי-התאמה היא סימן חיובי.
3. חוסר עקביות ב-Permissions API
דפדפנים אמיתיים מחזירים תוצאות קוהרנטיות כשמתשאלים אותם דרך Permissions API. navigator.permissions.query({name: 'notifications'}) אמור להחזיר 'default' אם המשתמש לא העניק או שלל הרשאה במפורש.
Chrome headless מחזיר 'denied' כברירת מחדל, כי אין ממשק להצגת בקשת הרשאה. מסגרות אוטומציה מטליאות את זה — אך לעיתים קרובות באופן שגוי. סימן היכר נפוץ: תשאול הרשאה חריגה כמו 'clipboard-read' מחזיר תוצאה שאינה תואמת את מה ש-Chrome אמיתי עושה בגרסה הנוכחית.
4. מערכי תוספים וטיפוסי MIME
navigator.plugins בדפדפן אמיתי מחזיר PluginArray עם רשומות כמו מציג ה-PDF ותוסף ה-PDF של Chromium. במצב headless המערך הזה ריק.
תוספי stealth מוסיפים רשומות מזויפות, אך לרשומות יש לעיתים קרובות מאפיינים שגויים — length חסר, שדות description שגויים, או אובייקטי תוסף שלא מתנהגים כמופעי Plugin כשבודקים אותם עם instanceof.
5. אי-התאמות שפה ואזור
navigator.language ו-navigator.languages אמורים להתאים לכותרת ה-HTTP Accept-Language. הם אמורים גם להיות עקביים עם אזור הזמן שמדווח על ידי Intl.DateTimeFormat().resolvedOptions().timeZone.
בוט שטוען שהוא בקייב (אזור זמן Europe/Kyiv) אך שולח Accept-Language: en-US,en;q=0.9 ומדווח navigator.language === 'en-US' אפשרי אך חריג סטטיסטית. בשילוב עם IP ממרכז נתונים בפרנקפורט, התמונה מתבהרת.
6. מחרוזות מנוע WebGL
WebGL2RenderingContext.getParameter(WebGLDebugRendererInfo.UNMASKED_RENDERER_WEBGL) מחזיר את יצרן ה-GPU והדגם. במכונה אמיתית זה עשוי להיות:
ANGLE (Intel, Intel(R) UHD Graphics 620 Direct3D11 vs_5_0 ps_5_0)
Chrome headless שרץ במכולה מחזיר לעיתים קרובות:
ANGLE (Google, Vulkan 1.3.0 (SwiftShader Device (Subzero)), SwiftShader driver)
כל מחרוזת GPU המכילה SwiftShader או Subzero היא סימן בוט חזק.
7. חריגות מסך ו-viewport
לחלון דפדפן אמיתי יש window.outerWidth ו-window.outerHeight שמביאים בחשבון את ה-chrome של הדפדפן — שורת כתובת, לשוניות, סרגל סימניות. ההפרש בין המידות החיצוניות לפנימיות הוא בדרך כלל 80–140 פיקסלים אנכית.
לדפדפנים headless יש לעיתים קרובות outerHeight === innerHeight כי אין chrome להצגה. Puppeteer עם headless: false מתקן את זה, אך הרבה תצורות אוטומציה עדיין רצות עם מידות שוות.
8. מניית גופנים
למשתמשים אמיתיים יש מאות גופנים מותקנים, המשתנים לפי מערכת הפעלה, שפה ואפליקציות מותקנות. Windows 11 עם Office מותקן כולל 500+ גופנים. מכולות אוטומציה שרצות Alpine Linux כוללות 30.
מניית גופנים דרך document.fonts.check() או רינדור canvas מחוץ למסך חושפת זאת מיד. דפדפן שמדווח על עצמו כ-Windows 11 Chrome אך חושף רק 30 גופנים הוא כמעט בוודאות אוטומציה.
9. אנטרופיית תנועת עכבר
משתמשים אמיתיים מזיזים עכבר עם רעד. עקומות אינן ליניאריות. המהירות משתנה. בין שתי לחיצות יש בדרך כלל מאות אירועי mousemove.
מסגרות אוטומציה מסנתזות תנועה — או עם קווים ישרים לקואורדינטות, או עם עקומות מתוכנתות שחסרות את הרעידות הזעירות של יד אדם. אפילו סימולציות של עקומות Bezier נוטות לפרופילי האצה חלקים באופן חשוד.
מערכות זיהוי בוטים אוספות עקבות תנועה ומנקדות אותן מול מודל של תנועה אנושית. עקבות עם מהירות עקבית מדי, עקומות נקיות מדי, או מיקרו-אירועים חסרים מסומנים.
10. חתימות תזמון
מסגרות אוטומציה מריצות JavaScript עם מאפייני תזמון שונים מדפדפנים מונעי-משתמש. תזמון performance.now() בין אירועים, במיוחד בין pointerdown ל-pointerup, עוקב אחר התפלגות שונה.
זמני החזקת קליק אמיתיים הם 50–150ms עם שונות גבוהה. החזקת הקליק של Puppeteer כברירת מחדל היא ערך קבוע — לעיתים קרובות 30ms או 100ms — כמעט בלי שונות. מאה לחיצות עם זמני החזקה זהים הן אוטומציה חד-משמעית.
11. אי-התאמת טביעת אצבע TLS
זה עוקף את JavaScript לחלוטין. כאשר Puppeteer מתחבר, הוא משתמש במחסנית ה-TLS של Chromium — זהה ל-Chrome אמיתי. אך גורדים מבוססי-undici או מבוססי-axios שמעמידים פנים שהם Chrome דרך זיוף user agent משתמשים במחסנית ה-TLS של Node.js, שיש לה חתימת Client Hello שונה במובהק (hash JA4).
בקשה שטוענת User-Agent: Chrome/124.0.6367.60 אך מציגה טביעת אצבע TLS של Node.js היא אישור מיידי ללקוח שאינו דפדפן.
12. דליפת פרוטוקול CDP
Puppeteer ו-Playwright מתקשרים עם Chrome דרך Chrome DevTools Protocol. בתצורות מסוימות הפרוטוקול הזה מותיר עקבות ניתנים לזיהוי — מאפיינים נוספים על אובייקט Runtime, פורמטי Error.stack שהשתנו, או פלטי console.debug ספציפיים שמופיעים רק כשלקוח CDP מחובר.
הפקודה Runtime.enable בפרט משנה את אופן הרינדור של עקבות מחסנית. בוטים שלעולם לא מפעילים שגיאות נמנעים מהסימן הזה, אך כל תהליך אוטומציה שמגיע לחריגה (ורובם מגיעים) מותיר טביעות אצבע.
שכבות של הסימנים הללו
אף סימן בודד אינו הוכחה לאוטומציה. משתמש אמיתי עם דרייבר GPU חריג עשוי להחזיר מחרוזת WebGL מוזרה. משתמש ממוקד-פרטיות עשוי לשנות את navigator.plugins.
עוצמתו של הזיהוי המודרני נובעת משילוב סימנים — 12 סימנים חלשים חזקים יותר מסימן חזק אחד, כי משתמש אמיתי כמעט אף פעם לא מפעיל יותר מ-2–3 חריגות בו-זמנית.
מערכות זיהוי מקצות לכל סימן משקל ומחשבות ציון. מעל הסף, המבקר מטופל כאוטומציה. הסף המדויק והמשקלים הם החלקים הקשים ביותר לכוונן נכון — מחמירים מדי וחוסמים משתמשים לגיטימיים; סלחניים מדי ובוטים מתוחכמים חומקים.
הסימנים עצמם הם החלק הקל. כמעט כל מסגרת אוטומציה מדליפה את כל ה-12. ההנדסה האמיתית היא בהחלטה אילו שילובים חשובים, אילו הם תוצאות חיוביות שגויות, וכיצד לעדכן את המודל ככל שמסגרות האוטומציה מסתגלות.