Система обнаружения ботов TRACIO выявляет автоматизированные браузеры, headless-инструменты и скриптовые атаки с помощью двухслойной архитектуры: клиентский сбор сигналов и серверный анализ. Она обнаруживает распространённые фреймворки автоматизации, headless-браузеры, ИИ-агенты для веб-сёрфинга и антидетект-браузеры с почти нулевым уровнем ложных срабатываний, автоматически добавляя легитимные поисковые краулеры в allowlist.
Браузерный агент собирает набор специфичных для ботов и кросс-валидационных сигналов, которые поступают в серверные решения. Идентификаторы сигналов ниже приведены для иллюстрации — внутренние идентификаторы не являются стабильным публичным контрактом:
| Сигнал | Описание |
|---|---|
s300 (webdriver) | Флаг navigator.webdriver — устанавливается в true фреймворками автоматизации |
s301 (eval length) | eval.toString().length — Chrome/Edge: 33, Firefox: 37. Другие значения указывают на инструментирование. |
s305 (bot composite) | Сканирует window/document на 14 артефактов фреймворков автоматизации |
s97 (headless markers) | Артефакты CDP, разрешения на уведомления, число плагинов, внешние размеры |
s157 (automation scan) | Комплексное сканирование на Selenium, Puppeteer, PhantomJS, Playwright и ещё 10 |
s158 (iframe webdriver) | Создаёт скрытый iframe и читает navigator.webdriver внутри него. Ловит инструменты, которые патчат основной фрейм, но забывают про iframe. |
s159 (native function) | Удаляет Function и Object, проверяет, действительно ли геттеры нативны. Обнаруживает инструментирование на основе Proxy. |
s155 (window CRC32) | Сканирование хеша CRC32 каждого свойства window для обнаружения внедрённых глобалов автоматизации |
s163 (devtools trap) | Ловушка через геттер console — обнаруживает, когда DevTools активно открыты |
Сервер запускает набор детекторов, чьи взвешенные выходы объединяются в
bot-score. Score сопоставляется с вердиктом по порогам — bot, когда он ≥ 0.70,
suspicious, когда ≥ 0.30, иначе human, — а сигнал жёсткого отказа (такой как
navigator.webdriver) сразу форсирует вердикт bot. Вердикты bot несут
произвольную метку bot_type.
Репрезентативные детекторы и их эффект:
| Детектор | Эффект |
|---|---|
| Флаг Webdriver | Hard-fail → bot (тип webdriver) |
| Композитное сканирование автоматизации/фреймворков | Высокий вес → bot (тип {framework}) |
| Headless-маркеры | Взвешенно → bot (тип headless) |
| Аномалия eval-length | Взвешенно → bot (тип unknown) |
| Паттерн UA плохого бота | Взвешенно → bot (тип {label}) |
| Верифицированный хороший бот (обратный DNS) | Allowlist → human |
| Поведенческий (частота / энтропия / взаимодействие) | Взвешенно → bot (тип rateBot) |
| VM (Enterprise) · Эмулятор · DevTools (Business+) | Взвешенные сигналы устройства/вмешательства |
Тариф и доступность. Обнаружение DevTools работает на Business и Enterprise; обнаружение VM — Enterprise; обнаружение эмуляторов работает на всех планах. Всё это работает в веб-агенте. Обнаружение root, jailbreak, клонированных/сдвоенных приложений, Frida и заводского сброса зависит от нативных мобильных сигналов и неэффективно в web-only-развёртывании (в разработке). Полагайтесь на вердикты по автоматизации, headless, ИИ-агентам, антидетекту, VM/эмуляторам и поведению, которые полностью активны в вебе.
Обнаружение ботов записывает поле bot_result в каждое событие (и bot.result в
payload вебхука).
bot_result — одно из трёх канонических значений:
| Результат | Описание |
|---|---|
human | Индикаторы автоматизации не найдены. Верифицированные поисковые краулеры также разрешаются в human. |
bot | Обнаружен автоматизированный или скриптовый доступ. Сопровождается произвольной меткой bot_type. |
uncertain | Смешанные или слабые сигналы — ни явно человек, ни явно автоматизация. |
Это сопоставляется с бизнес-полем decision (real, fake или suspicious),
используемым по всему дашборду, и с bot.result в
payload вебхука.
"bot")bot_type — произвольная строка. Значения ниже — иллюстративные примеры того,
что выдают детекторы, а не исчерпывающий enum:
| Тип | Метод обнаружения |
|---|---|
webdriver | navigator.webdriver равно true |
selenium | Обнаружены специфичные для Selenium свойства window/document |
puppeteer | Совпали headless-индикаторы Puppeteer/Chromium |
playwright | Обнаружены специфичные для Playwright маркеры или строка UA |
headless | Сработали 2+ из 11 headless-маркеров |
phantomjs | Обнаружены глобалы PhantomJS (callPhantom, _phantom) |
nightmare | Обнаружены глобалы NightmareJS |
unknown | Аномалия eval length или иные неспецифичные индикаторы автоматизации |
rateBot | Поведенческий анализ частоты превысил пороги |
Наиболее прямое обнаружение. Все основные фреймворки автоматизации устанавливают navigator.webdriver = true согласно спецификации WebDriver. Это проверяется как в основном фрейме (s300), так и в скрытом iframe (s158), чтобы поймать инструменты, патчащие только основной фрейм.
Комплексное сканирование свойств window и document на специфичные для фреймворков артефакты:
| Фреймворк | Обнаруживаемые свойства |
|---|---|
| Selenium | _Selenium_IDE_Recorder, _selenium, calledSelenium, __selenium_evaluate, $cdc_* (ChromeDriver) |
| Puppeteer | domAutomation, domAutomationController, HeadlessChrome UA |
| Playwright | __playwright, флаг automation-controlled |
| PhantomJS | callPhantom, _phantom |
| NightmareJS | __nightmare, nightmare |
| CefSharp | CefSharp |
| Awesomium | awesomium |
| WebDriverIO | wdioElectron |
| Cypress | свойство window __cypress |
Сканирование также использует regex-паттерн /^([a-z]){3}_.*_(Array|Promise|Symbol)$/ для обнаружения внедрённых Selenium вспомогательных глобалов.
Оценивается 11 маркеров, порог классификации — 2 или больше:
| Маркер | Сигнал | Условие |
|---|---|---|
| webdriver | s300 | = "true" |
| отсутствуют vendor flavors | s28 | Нет объектов chrome/safari на window |
| CDP headless chrome | s305 | headlessChrome = true |
| notification denied | s88 | Notification.permission = "denied" при первом визите |
| chrome no runtime | s89 | window.chrome существует, но chrome.runtime отсутствует |
| один язык | s90 | navigator.languages.length <= 1 |
| экран по умолчанию 800x600 | s5 | Разрешение по умолчанию Headless Chrome |
| HeadlessChrome UA | UA | User-Agent содержит "HeadlessChrome" |
| нулевые внешние размеры | s97 | outerWidth = 0 и outerHeight = 0 |
| WebGL SwiftShader | s70 | Renderer содержит "swiftshader" (программный GPU) |
| WebGL llvmpipe | s70 | Renderer содержит "llvmpipe" (программный GPU) |
Известные «хорошие» боты распознаются по паттерну User-Agent. Каждый верифицируется через обратный DNS для предотвращения подделки:
.googlebot.com)| Бот | Разрешённые домены |
|---|---|
.googlebot.com, .google.com | |
| Bing | .search.msn.com |
| Apple | .applebot.apple.com |
| Yahoo | .crawl.yahoo.net |
| Yandex | .yandex.com, .yandex.net, .yandex.ru |
| DuckDuckGo | .duckduckgo.com |
Если запрос заявляет, что он Googlebot, но обратный DNS его IP разрешается в чужой домен (подтверждённое несоответствие PTR, а не просто отсутствующий/истёкший по таймауту запрос), он классифицируется как bot (тип spoofed:google).
Обнаружение на основе частоты с оценкой энтропии:
| Частота (req/min) | Порог энтропии | Действие |
|---|---|---|
| 120+ | < 0.1 | bot (type: rateBot) |
| 60-120 | < 0.3 | bot (type: rateBot) |
| 45-60 | < 0.1 | bot (type: rateBot) |
| < 45 | Любая | Норма |
Дополнительное правило: отсутствие событий мыши/клавиатуры после 30+ секунд возраста сессии классифицирует визит как bot с типом noInteraction.
eval.toString().length даёт согласованные значения для каждого движка:
Любое другое значение указывает, что функция eval была перехвачена фреймворком автоматизации, и классифицирует визит как bot с типом unknown.
Клиентский SDK предоставляет удобный булев флаг result.bot.detected:
const result = await tracio.getResult()
if (result.bot.detected) { // Log and block console.warn(`Bot detected (confidence ${result.bot.confidence})`) showCaptcha() return}
// Proceed with loginawait login(credentials)На своём сервере действуйте по доставке вебхука. Обнаружение ботов
находится в объекте bot — bot.result и bot.type:
// `event` is the webhook delivery body (/docs/webhooks)app.post("/webhook/tracio", async (req, res) => { const event = req.body
if (event.bot.result === "bot") { await db.blockedRequests.insert({ visitorId: event.visitorId, botType: event.bot.type, ip: event.ip, timestamp: new Date(), }) }
res.status(200).send("OK")})Обнаружение ботов TRACIO спроектировано для почти нулевого уровня ложных срабатываний. Однако граничные случаи возможны:
| Сценарий | Риск | Меры |
|---|---|---|
Расширения браузера, изменяющие navigator | Низкий | Кросс-валидация нескольких сигналов предотвращает срабатывание по одному сигналу |
| Корпоративное защитное ПО | Очень низкий | Обнаружение headless требует 2+ маркеров |
| Инструменты доступности | Отсутствует | API доступности не запускают никаких сигналов обнаружения |
| Пользователи VPN/прокси | Отсутствует | Использование VPN отслеживается отдельно от обнаружения ботов |
Если вы столкнулись с ложными срабатываниями, изучите поле bot.type, чтобы понять, какой детектор сработал, и скорректируйте свою политику соответственно.