Типичная картина: антифрод-система докладывает, что всё чисто — JavaScript-проверки пройдены, User-Agent нормальный, куки есть, canvas fingerprint совпадает. И при этом 30% бюджета уходит на клики, которые никогда не превратятся в заявки. Как так получается? Ответ — headless-браузеры.
Что такое headless-браузер
Headless-браузер — это полноценный браузер (Chrome, Firefox), запущенный без графического интерфейса. Он загружает страницы, выполняет JavaScript, обрабатывает cookie, рендерит DOM — делает всё то же самое, что обычный браузер, просто без окна на экране.
Изначально headless-режим создавался для разработчиков: автоматизированного тестирования, скриншотов страниц, CI/CD пайплайнов. Но те же инструменты отлично работают для автоматизации кликов по рекламе.
Главные игроки:
- Puppeteer — библиотека от Google для управления Chrome через DevTools Protocol. Стандарт индустрии.
- Playwright — более современный инструмент от Microsoft, поддерживает Chrome, Firefox и WebKit.
- Selenium — старейший фреймворк, поддерживает любой браузер через WebDriver.
- Антидетект-браузеры — Multilogin, Dolphin Anty, AdsPower. Форки Chrome с патчами на уровне C++ кода.
Почему headless так сложно поймать на JS-проверках
Когда антифрод-система проверяет посетителя через JavaScript, она задаёт браузеру вопросы: User-Agent, размер экрана, WebGL, canvas fingerprint, WebRTC, движение мыши, куки. Headless Chrome честно отвечает на все эти вопросы — потому что это настоящий Chrome. Тот же движок Blink, тот же V8, та же реализация WebGL.
Проблема navigator.webdriver
Исторически headless-браузеры выдавали себя одним флагом: navigator.webdriver === true. Антифрод-системы начали его проверять. Операторы ботов ответили просто:
// Отключить флаг webdriver в Puppeteer
await page.evaluateOnNewDocument(() => {
Object.defineProperty(navigator, 'webdriver', {
get: () => undefined
});
});
Один вызов — и детектор больше не видит headless.
puppeteer-extra-plugin-stealth
Сейчас существуют целые библиотеки для маскировки. Самая популярная — puppeteer-extra-plugin-stealth. Патчит более 10 свойств браузера: скрывает navigator.webdriver, подделывает navigator.plugins, исправляет Permissions API, добавляет поддельный WebGL Vendor/Renderer, имитирует window.chrome объект.
После этого плагина большинство JS-детекторов не видят headless Chrome вообще.
Что реально выдаёт headless: сетевой уровень
При всей изощрённости маскировки у headless-браузеров есть уязвимости, которые невозможно закрыть патчингом JavaScript.
TCP fingerprint не совпадает с профилем
Бот запускает headless Chrome на сервере с Ubuntu 22.04. Puppeteer-stealth скрывает все JS-признаки, User-Agent установлен в Chrome 120 on Windows 11. Но:
- TCP Window Size при первом SYN-пакете = 65535 (Linux), а у реального Windows 11 = 64240
- TTL исходящих пакетов = 64 (Linux), а не 128 (Windows)
- TCP Options в SYN — Linux-порядок, отличный от Windows
Эти параметры формируются ядром Linux на сервере, и никакой JavaScript их не изменит.
JA3/JA4 TLS fingerprint
При установке HTTPS-соединения браузер отправляет TLS ClientHello — набор cipher suites, расширений и их порядок строго специфичен для каждого браузера. Chrome на Linux через Puppeteer имеет другой JA3 fingerprint, чем Chrome на Windows нативно. Сигнатура «Chrome 120 on Linux» при заявленном Windows — красный флаг.
HTTP/2 SETTINGS fingerprint
При установке HTTP/2 соединения клиент отправляет SETTINGS фрейм с уникальными параметрами. Значения и порядок различаются для каждого браузера и ОС. Никакой JS-патч не изменит, как сетевой стек формирует HTTP/2 фреймы.
Иерархия надёжности методов детекции
| Метод | Надёжность | Обходится? |
|---|---|---|
| User-Agent | Очень низкая | Тривиально |
| IP-блокировка | Низкая | Легко (прокси) |
| navigator.webdriver | Низкая | puppeteer-stealth |
| Canvas/WebGL fingerprint | Средняя | Антидетект-браузеры |
| Поведенческий анализ | Средняя | Имитация движений |
| TCP fingerprint | Высокая | Требует патч ядра ОС |
| TLS (JA3/JA4) fingerprint | Высокая | Требует кастомного браузера |
| Комбинация сетевых отпечатков | Очень высокая | Практически невозможно |
Итоги
Барьер входа в headless-фрод стал минимальным: Docker-контейнер, 10 VPS, ротация прокси — дело нескольких часов для любого джуниор-разработчика при стоимости инфраструктуры $50–200 в месяц.
Именно поэтому JS-детекция как основной метод защиты обречена. Атакующий всегда может изучить, что именно проверяется, и адаптировать бот. Сетевой стек — нет. Детекция должна начинаться на уровне TCP/TLS — до того, как браузер успел выполнить хоть строчку JavaScript.