CDP-витоки Puppeteer: як антифрод бачить автоматизацію

Puppeteer зручний, бо напряму говорить із Chromium через Chrome DevTools Protocol. Але ця зручність має ціну. CDP дає майже повний контроль над браузером і водночас залишає технічні сліди: стек викликів, порти відладки, дивні таймінги, ранні ін'єкції JavaScript.
Якщо ви парсите дані, тестуєте захищені сайти або працюєте з акаунтами, одного stealth-плагіна часто вже недостатньо. Антифрод перевіряє відбиток браузера разом зі способом керування ним. Тут починається зона детекції WebDriver і відбитків веб-скрапінгу.
Що таке CDP і чому він світиться
Chrome DevTools Protocol створили для відладки, профілювання і керування браузером. Puppeteer використовує його як командний канал: відкрити сторінку, виконати скрипт, клікнути, прочитати DOM, зробити screenshot.
Розробнику це зручно. Антифроду також, тільки з іншого боку. Протокол створює маркери, яких у звичайній людській сесії часто немає: локальний канал керування, специфічні execution context, сліди Runtime.evaluate, нетиповий життєвий цикл сторінки.
| Маркер | Де проявляється | Чому підозрілий |
|---|---|---|
pptr:evaluate | Stack trace помилок | Прямо вказує на Puppeteer |
| DevTools port | Локальний host і WebSocket | Показує віддалене керування браузером |
| Ранній JS-патч | document_start | Ламає природний таймінг завантаження |
| Headless ознаки | ClientRects, шрифти, GPU | Видають штучне середовище |
| User-Agent mismatch | HTTP headers vs navigator | Видно розсинхрон середовища |
Класичний Canvas fingerprinting або WebGL fingerprint допомагає зрозуміти, що це за пристрій. CDP-витоки показують інше: чи не смикає цей пристрій скрипт. Для антифроду це часто сильніший сигнал.
Де Puppeteer залишає найпомітніші сліди
Перший помітний шар — execution context. Коли ви викликаєте page.evaluate(), Puppeteer передає код у браузер через CDP. Якщо антифрод спровокує помилку і прочитає Error.stack, у стеку може вилізти не логіка сайту, а технічний слід автоматизації.
Далі йдуть ін'єкції через evaluateOnNewDocument. Багато stealth-плагінів дуже рано переписують navigator.webdriver, WebGL, Canvas, plugins, languages. Але важкий патч до нормального старту сторінки теж виглядає дивно. Людина не приходить на сайт із набором JS-перехоплювачів у головному контексті.
Ще один слід — локальні порти. Якщо Chromium запущений із DevTools endpoint, захисний скрипт може простукувати локальні адреси або ловити непрямі таймінги. Тут потрібен захист від сканування портів, а не чергова підміна User-Agent.
Чому зміна User-Agent не рятує
Зміна User-Agent через CDP править тільки маленький шматок картини. Сервер бачить один заголовок, JavaScript читає властивості браузера, Client Hints додають ще один шар, а GPU і сенсори можуть показати зовсім іншу історію.
Якщо HTTP каже "мобільний браузер", а поведінкові API показують desktop-курсор, відсутність touch-патернів і дивні Client Hints, антифрод не повірить заголовку. Він побачить розсинхрон. Спуфінг пристрою має бути узгодженим, косметика тут швидко сиплеться.
| Поганий підхід | Кращий підхід |
|---|---|
| Міняти тільки User-Agent | Узгоджувати UA, Client Hints, viewport, timezone, touch |
| Вантажити великий stealth на старті | Розділяти ранні й пізні патчі |
| Покладатися на один плагін | Перевіряти стек, порти, таймінги, fingerprint |
| Запускати все в headless | Тестувати headful або спеціалізоване середовище |
Антифрод рідко ловить одну-єдину помилку. Зазвичай він збирає кілька дрібних невідповідностей.
Як знизити ризик CDP-детекту
Абсолютної невидимості немає. Зате можна прибрати грубі сліди: не відкривати TCP-порт відладки без потреби, не залишати pptr:evaluate у стеку, не заливати важкі stealth-патчі в першу мілісекунду, не ламати узгодженість між fingerprint-шарами.
Для простих задач цього іноді вистачає. Для серйозного збору даних, акаунт-операцій або сайтів із сильним антифродом потрібна інша архітектура: не JS-косметика поверх Chromium, а середовище, де fingerprint і автоматизація продумані нижче рівня сторінки.
Тут доречний антидетект-браузер. Afina дає ізольовані браузерні профілі, per-account proxy, керування проксі, автоматизацію дій, локальний API і RPA-сценарії. Команді простіше керувати такою базою, ніж після кожного оновлення латати node_modules.
Коли Puppeteer ще доречний
Puppeteer не треба списувати. Він добре підходить для внутрішнього QA, тестування власних сайтів, простого збору відкритих даних і перевірки рендерингу. Проблеми починаються там, де сайт сам активно шукає автоматизацію.
Один тест на власному домені навряд чи впирається в CDP-витоки. А от десятки профілів, повторювані дії, проксі-ротація і захищені форми вже вимагають ширшої картини: headless browsing, TLS fingerprinting, поведінка, сесії, командний контроль. Puppeteer тут лише частина задачі.
Afina корисна саме в другому випадку. Вона не замінює інженерне рішення, зате дає стабільну базу: профілі, proxy-per-account, сценарії, tasks, модулі, синхронізацію та контроль запусків. Почати можна з web scraping and data або одразу подивитися завантаження.
FAQ — Часті запитання
Що таке CDP-витоки в Puppeteer?
Це технічні сліди Chrome DevTools Protocol, які можуть показати сайту, що браузером керує скрипт. До них належать стек викликів, локальні порти, execution context і підозрілі таймінги ін'єкцій.
Чи достатньо puppeteer-extra-plugin-stealth?
Для простих сайтів іноді так. Для сильного антифроду ні, бо stealth-плагіни часто маскують очевидні прапорці, але не прибирають усі CDP-маркери.
Чому navigator.webdriver не єдина проблема?
Бо антифрод дивиться на багато шарів одночасно: стек, порти, Client Hints, WebGL, Canvas, таймінги, поведінку і мережеві ознаки. Один прапорець давно не вирішує всю задачу.
Чи можна зробити Puppeteer повністю невидимим?
Практично ні. Можна зменшити кількість грубих слідів, але будь-яка автоматизація має відхилення. Питання в тому, чи достатньо вони малі для конкретного сайту.
Як Afina допомагає в задачах автоматизації?
Afina дає ізольовані профілі, проксі на акаунт, fingerprint-логіку, локальний API і сценарії автоматизації. Це база для команд, яким треба керувати процесом, а не просто запускати Puppeteer у черговій вкладці.
