@brandup/browser-autodetect
v4.0.100
Published
Browser-automation detector: probes direct markers (navigator.webdriver, driver artifacts like cdc_*/__webdriver_*, HeadlessChrome UA, software WebGL, headless side-effects, hardware/screen, WebRTC) plus JS-environment tampering used to hide them (overrid
Maintainers
Readme
@brandup/browser-autodetect
Детектор автоматизации браузера. Определяет, управляется ли текущая страница автоматизированным или
инструментированным браузером (Selenium/WebDriver, Puppeteer, Playwright, headless Chrome), и не
вмешивались ли в её JS-окружение, чтобы это скрыть (stealth-плагины, privacy-расширения, ручные
патчи navigator/WebGL/canvas). Без зависимостей, работает в любом современном браузере.
Все используемые техники — общеизвестные в области fingerprinting / bot-detection. Детектор сочетает два слоя:
- Прямые маркеры автоматизации — то, что напрямую выдаёт драйвер/headless.
- Проверки целостности окружения — следы, которые остаются, когда маркеры из (1) пытаются замаскировать (подмена нативных функций, искажение canvas-отпечатка, рассогласование API).
Установка
npm i @brandup/browser-autodetectИспользование
ESM / бандлер
import { detect } from "@brandup/browser-autodetect";
const report = await detect();
console.log(report.automated ? "АВТОМАТИЗАЦИЯ" : "похоже на человека",
`score=${report.score} confidence=${report.confidence}`);
for (const s of report.detected)
console.log(`• [${s.category}] ${s.title} — ${s.detail}`);Быстрая синхронная проверка (без iframe/canvas/webrtc)
import { detectSync } from "@brandup/browser-autodetect";
const report = detectSync();Прямое подключение <script>
<script src="node_modules/@brandup/browser-autodetect/dist/autodetect.global.js" data-auto></script>
<!-- data-auto: автозапуск, отчёт в console и в window.BrandUpAutodetect.lastReport -->
<script>
BrandUpAutodetect.detect().then(r => console.log(r));
</script>API
detect(options?: DetectOptions): Promise<DetectionReport> // полная проверка
detectSync(): DetectionReport // только синхронные сигналы
interface DetectOptions {
webrtc?: boolean; // WebRTC-зонд (ICE), по умолчанию true
iframe?: boolean; // контраст окружения iframe, по умолчанию true
canvas?: boolean; // canvas getImageData vs toDataURL, по умолчанию true
timeoutMs?: number; // бюджет WebRTC-зонда, по умолчанию 600
}
interface DetectionReport {
automated: boolean; // вердикт
score: number; // 0..100 — вероятностное ИЛИ весов сигналов
confidence: "low"|"medium"|"high";
signals: Signal[]; // все проверенные признаки
detected: Signal[]; // только сработавшие
userAgent: string;
timestamp: number;
}Балл = 1 − ∏(1 − weight) по сработавшим сигналам: несколько слабых складываются, один
определяющий (weight ≥ 0.85) сразу даёт вердикт. automated — если есть определяющий сигнал
либо score ≥ 60.
Что проверяется
Прямые маркеры
| Сигнал (id) | Категория | Что выдаёт |
|---|---|---|
| navigator.webdriver | webdriver | Браузер под WebDriver (navigator.webdriver === true) |
| automation.artifacts | webdriver | Глобалы драйверов: cdc_*/$cdc_ (ChromeDriver), __webdriver_*/__selenium_* (Selenium), __playwright/__puppeteer, _phantom/callPhantom и т.п. |
| webgl.software-renderer | webgl | Программный рендер (SwiftShader/llvmpipe) вместо GPU — серверный headless |
| useragent.headless, useragent.client-hints-headless | useragent | Маркер HeadlessChrome в UA / client hints |
| window.outer-equals-inner | headless | outerHeight === innerHeight — нет рамки/тулбара окна |
| navigator.plugins-empty, navigator.languages-empty | headless | Пустые plugins/languages |
| window.chrome-missing | headless | Нет window.chrome при Chrome-UA |
| screen.no-taskbar, screen.odd-color-depth | hardware | availHeight === height, нестандартный colorDepth |
| navigator.hardwareConcurrency | hardware | Серверное число ядер (≥24) |
| webrtc.no-host-candidates, webrtc.unavailable | webrtc | WebRTC отключён/проксирован — нет host/mDNS ICE-кандидатов |
| css.reduced-motion | environment | prefers-reduced-motion: reduce |
Проверки целостности окружения (антиспуфинг)
Срабатывают, когда нативный API переопределён или canvas/permissions искажены, чтобы скрыть автоматизацию:
| Сигнал | Что ловит |
|---|---|
| native-integrity:* | Переопределённый нативный метод/геттер сообщает [native code], но владеет собственным prototype — нативные методы так не делают. Покрывает Function.prototype.toString, getParameter, toDataURL/toBlob/getImageData, hasFocus, permissions.query, window.open, Object.getOwnPropertyNames/keys, Reflect.ownKeys, getChannelData, геттеры outer*/avail*/hardwareConcurrency. |
| canvas.todataurl-noise | «Canvas poisoning»: toDataURL (lossless PNG) декодируется в пиксели, отличные от getImageData (антифингерпринт-расширения/стелс шумят вывод, не трогая getImageData). |
| permissions.query-type | query({name:"notifications"}) вернул не PermissionStatus или рассогласован с Notification.permission. |
| iframe.realm | В окружении свежего iframe те же нативные API подменены — инструментирование, распространённое на дочерние окна. |
| window.chrome.runtime-present | chrome.runtime присутствует на обычной странице (вне расширения его не бывает) — вероятна подстановка. |
Сборка
npm run build # esbuild → dist/{index.js,index.cjs,autodetect.global.js} + tsc → *.d.ts
npm test # node:test, ядро без браузера
npm run typecheck # tsc --noEmitDOM-зонды (canvas/iframe/webrtc) тестируются вручную в браузере — открой страницу и вызови
detect(). Юнит-тесты покрывают инвариант целостности нативных функций и агрегацию вердикта.
Пределы
- TLS/HTTP2-фингерпринт, реальные шрифты и физическая растеризация — вне досягаемости JS, здесь не проверяются.
- Сигналы
headless/hardwareпо отдельности слабые: реальные окружения тоже бывают без плагинов или с урезанным экраном. Сила — в комбинации и в проверках целостности окружения. - Детектор не делает сетевых запросов и не передаёт отчёт — всё считается локально на странице.
- Совершенный спуфинг (например, подмена нативных методов через
Proxyбез собственногоprototypeи зеркалирование canvas-шума вgetImageData) может обойти антиспуфинг-слой; ни один клиентский детектор не даёт абсолютной гарантии.
Лицензия
MIT
