Архитектура системы, которая создаёт Chromium-профили, неотличимые от реальных пользователей при проверке скриптами антифрода Яндекса (tmgrdfrend.fp.js, watch_serp.js, SmartCaptcha). Документ покрывает все 60+ параметров фингерпринта, систему прокси, поведенческие модели и валидацию.
Источники анализа: реверс-инжиниринг tmgrdfrend.fp.js (183 KB, 100+ параметров), анализатор pager-syzran.ru (60 проверок), мониторинг sosiska.top (версионирование скриптов), отчёт urlquery.net (сетевые запросы SmartCaptcha), архитектура Network/Monstro.
Ключевой принцип — шаблон + контролируемая мутация. Мы НЕ клонируем отпечатки реальных пользователей. Если Яндекс увидит один и тот же fingerprint hash с двух разных IP — это ещё подозрительнее синтетики. Вместо этого база реальных снапшотов даёт нам валидные комбинации параметров, а генератор создаёт на их основе уникальный профиль, которого раньше не существовало, но который выглядит так, будто мог бы существовать.
Snapshot Collector Profile Generator Runtime Engine ┌────────────────────┐ ┌──────────────────────┐ ┌──────────────────────┐ │ Реальные машины │ │ Template Selector │ │ Chromium Fork │ │ (Win/Mac/Linux) │──собирает───▶│ выбор шаблона │──generates──▶│ (патченный бинарник) │ │ │ валидные │ │ уникальный │ │ │ Что собираем: │ комбинации │ Mutation Engine │ профиль │ Behavior Engine │ │ • GPU + extensions │ │ canvas/audio seed │ │ (ИИ-сценарии) │ │ • Шрифты по OS │ │ ±шрифты, viewport │ │ │ │ • Voices по OS │ │ уникальный hash │ │ Cookie/State Store │ │ • Screen + DPR │ │ │ │ (персистентный) │ │ • Plugins + MIME │ │ Proxy Binder │ │ │ │ • WebGL precision │ │ IP → geo → TZ │──binds──────▶│ WebRTC TURN │ └────────────────────┘ └──────────┬───────────┘ └──────────┬───────────┘ │ │ ▼ ▼ ┌──────────────────────┐ ┌──────────────────────┐ │ Consistency │ │ Runtime Validator │ │ Validator │ │ & Script Monitor │ │ │ │ │ │ Все параметры │ │ Trust Score ≥ 85 │ │ согласованы? │ │ перед каждым запуском│ │ Уникальность hash? │ │ Мониторинг fp.js │ └──────────────────────┘ └──────────────────────┘
Собирает с реальных машин (физических и облачных) валидные комбинации параметров: какой GPU идёт с какими WebGL extensions, какие шрифты стоят на конкретной OS, какие voices даёт Windows 11 vs macOS. Это не отпечатки для копирования — это эталоны валидных связок.
Берёт шаблон из базы и создаёт уникальный профиль: canvas seed (→ уникальный hash), audio seed, ±2-3 шрифта, рандомный viewport в реалистичном диапазоне. Результат — профиль, которого раньше не было, но все параметры внутренне согласованы.
Патченный Chromium с подменой параметров на уровне C++ (не JS-инъекция). Canvas noise, WebGL vendor/renderer, Audio — всё через нативные хуки в движке Blink/Skia.
Проверяет, что все параметры профиля согласованы: GPU ↔ Canvas hash, OS ↔ шрифты ↔ voices, TZ ↔ IP geo, UA ↔ platform. Также проверяет уникальность — два профиля не должны дать одинаковый fingerprint.
Три подхода к созданию профилей — и почему работает только третий:
| Подход | Как работает | Проблема | Результат |
|---|---|---|---|
| Рандом | Случайная генерация каждого параметра отдельно | Нарушение внутренних связей. Intel GPU + 0 расширений WebGL. Linux + шрифты Segoe UI | Trust Score 20-40 |
| Клон | Точное копирование отпечатка реального пользователя | Дубликат fingerprint hash с другого IP. Яндекс видит: «этот отпечаток уже есть в Москве, а теперь появился в Новосибирске». Красный флаг | Детекция дубля |
| Шаблон + мутация | Берём реальную комбинацию параметров как шаблон, мутируем уникальные компоненты (canvas seed, audio seed, ±шрифты, viewport). Все связи сохранены, hash уникален | — | Trust Score 85-95 |
Эти параметры жёстко связаны друг с другом. Менять один без остальных нельзя — нарушится консистентность:
Эти параметры делают каждый профиль уникальным, не нарушая связей:
// Шаблон: "Win11-Intel-1080p"
// Ядро (одинаковое):
{
os: "Windows",
platform: "Win32",
gpuVendor: "Intel Inc.",
gpuRenderer: "ANGLE (Intel, Intel(R) UHD Graphics 630 ...)",
glExtensions: ["GL_EXT_blend_minmax", "GL_EXT_color_buffer_half_float", ...], // 35 шт.
basefonts: ["Arial", "Segoe UI", "Verdana", "Times New Roman", ...], // 62 шт.
voices: ["Microsoft David", "Microsoft Zira", "Microsoft Mark"],
mathPrecision: { acos: 1.4473588658278522, sin: 0.8178819121159085, ... },
cores: 8, memory: 8,
screen: { width: 1920, height: 1080, dpr: 1 },
}
// Профиль A (мутация): // Профиль B (мутация):
{ {
canvasSeed: "a7f2e9...", canvasSeed: "3bc841...",
audioSeed: "d4e1b3...", audioSeed: "f8a2c7...",
canvasHash: "38b43122", // ← уникальный canvasHash: "7c1df890", // ← другой
audioSum: 101.66, // ← уникальный audioSum: 87.23, // ← другой
fonts: basefonts + ["Consolas"] fonts: basefonts - ["Georgia"]
- ["Palatino Linotype"], + ["Cascadia Code"],
innerWidth: 1583, innerHeight: 847, innerWidth: 1412, innerHeight: 790,
outerHeight: 939, outerHeight: 882,
colorScheme: "dark", colorScheme: "light",
adblock: true, adblock: false,
permissions: { notifications: "denied" }, permissions: { camera: "denied" },
mediaDevices: [ mediaDevices: [
{ kind: "audioinput", id: "a1b2..." }, { kind: "audioinput", id: "x9y8..." },
{ kind: "audiooutput", id: "c3d4..." }, { kind: "videoinput", id: "z7w6..." },
], { kind: "audiooutput", id: "v5u4..." },
battery: { level: 0.73, charging: false }, ],
network: { downlink: 7.2 }, battery: { level: 1, charging: true },
} network: { downlink: 12.4 },
}
Для качественной генерации нужна база из 100+ реальных комбинаций параметров. Снапшоты собираются с реальных физических и облачных машин.
| Группа | Параметры | Как собираем |
|---|---|---|
| OS | Версия, билд, architecture, bitness | navigator.userAgentData.getHighEntropyValues() |
| GPU | Vendor, Renderer, WebGL version, extensions (полный список), precision (12 типов) | WebGL context: getParameter(UNMASKED_VENDOR_WEBGL), getSupportedExtensions(), getShaderPrecisionFormat() |
| Screen | width, height, availWidth, availHeight, colorDepth, DPR | screen.*, window.devicePixelRatio |
| Fonts | Полный список установленных шрифтов (100-200 кандидатов) | Canvas measureText() — проверяем каждый шрифт из списка, сравнивая с fallback |
| Voices | Список speechSynthesis голосов: name, lang, localService |
speechSynthesis.getVoices() |
| Plugins | Список плагинов и MIME types | navigator.plugins, navigator.mimeTypes |
| Media | Кодеки (canPlayType для h264/vp8/vp9/av1/h265/theora), Widevine | HTMLVideoElement.canPlayType(), navigator.requestMediaKeySystemAccess() |
| Math | Точность 16 функций (sin, cos, acos, tan, etc.), Float32 NaN byte | Прямой вызов Math.* с фиксированными аргументами |
| CPU / RAM | hardwareConcurrency, deviceMemory | navigator.* |
| Window objects | Наличие chrome, RTCPeerConnection, Notification, DeviceMotionEvent и др. | Проверка typeof window.X |
// Пример записи в hardware DB
{
id: "snap_001",
collected_at: "2026-08-15",
source: "physical", // physical | cloud | crowdsource
os: {
name: "Windows",
version: "10.0.19045", // Windows 10 22H2
platform: "Win32",
architecture: "x86",
bitness: "64",
},
gpu: {
vendor: "NVIDIA Corporation",
renderer: "ANGLE (NVIDIA, NVIDIA GeForce GTX 1660 SUPER Direct3D11 vs_5_0 ps_5_0, D3D11)",
webglVersion: "WebGL 1.0 (OpenGL ES 2.0 Chromium)",
shadingVersion: "WebGL GLSL ES 1.0 (OpenGL ES GLSL ES 1.0 Chromium)",
extensions: [
"ANGLE_instanced_arrays", "EXT_blend_minmax", "EXT_clip_control",
"EXT_color_buffer_half_float", "EXT_depth_clamp", "EXT_disjoint_timer_query",
// ... 38 total
],
precision: {
vertexShaderHighFloat: { rangeMin: 127, rangeMax: 127, precision: 23 },
vertexShaderMediumFloat: { rangeMin: 127, rangeMax: 127, precision: 23 },
// ... 12 types total
},
},
screen: { width: 2560, height: 1440, dpr: 1, colorDepth: 24 },
cpu: { cores: 12, memory: 16 },
fonts: ["Arial", "Arial Black", "Calibri", "Cambria", "Cascadia Code", "Comic Sans MS",
"Consolas", "Constantia", "Corbel", "Courier New", "Ebrima", "Gabriola",
"Georgia", "Impact", "Lucida Console", "Malgun Gothic", "Microsoft Sans Serif",
"Palatino Linotype", "Segoe UI", "Segoe UI Symbol", "Tahoma", "Times New Roman",
"Trebuchet MS", "Verdana", "Webdings", "Wingdings"], // 26 detected
voices: [
{ name: "Microsoft David - English (United States)", lang: "en-US", local: true },
{ name: "Microsoft Zira - English (United States)", lang: "en-US", local: true },
{ name: "Microsoft Irina - Russian (Russia)", lang: "ru-RU", local: true },
],
codecs: { h264: "probably", vp8: "probably", vp9: "probably", av1: "probably", h265: "", theora: "" },
widevine: true,
math: {
acos: 1.4473588658278522, acosh: 691.4686750787736, asin: 0.12343746096704435,
asinh: 0.881373587019543, atanh: 0.5493061443340549, atan: 0.4636476090008061,
sin: 0.8178819121159085, sinh: 1.1752011936438014, cos: -0.8390715290095377,
cosh: 1.5430806348152437, tan: -1.4214488238747245, tanh: 0.7615941559557649,
exp: 2.718281828459045, expm1: 1.7182818284590453, log1p: 2.3978952727983707,
powPI: 1.9275814160560206e-50,
},
float32NanByte: 255,
plugins: ["PDF Viewer", "Chrome PDF Viewer", "Chromium PDF Viewer",
"Microsoft Edge PDF Viewer", "WebKit built-in PDF"],
windowObjects: ["chrome", "RTCPeerConnection", "Notification",
"XPathResult", "DeviceMotionEvent", "Intl"],
}
| OS | GPU Vendor | Комбинаций | Приоритет |
|---|---|---|---|
| Windows 10 | Intel (UHD 620/630/770) | 5-8 | Высокий — самая частая комбинация в РФ |
| Windows 10 | NVIDIA (GTX 1050-1660, RTX 3060) | 5-8 | Высокий |
| Windows 11 | Intel (Iris Xe, UHD 770) | 5-8 | Высокий |
| Windows 11 | NVIDIA (RTX 3060-4060) | 3-5 | Средний |
| Windows 10/11 | AMD (RX 580, Vega) | 3-5 | Средний |
| macOS 13-14 | Apple (M1/M2/M3) | 5-8 | Средний |
| macOS 12-13 | Intel (Iris Plus/Pro) | 3-5 | Средний |
| Ubuntu 22/24 | Intel / NVIDIA | 3-5 | Низкий — мало пользователей в РФ |
Итого для MVP: 30-50 снапшотов. Для продакшена: 200+.
На основе анализа tmgrdfrend.fp.js — скрипта Яндекса, который собирает отпечаток браузера. Каждый параметр имеет критичность по степени влияния на Trust Score.
| Параметр | Что проверяет Яндекс | Критичность | Стратегия подмены |
|---|---|---|---|
navigator.userAgent |
Консистентность с userAgentData, версия Chrome, признаки Headless |
CRITICAL | Генерация на основе реальных UA-строк. Версия Chrome ±2 от текущей стабильной. Парсинг и кросс-валидация с brands |
navigator.userAgentData(High Entropy) |
brands[], platform, architecture, bitness, uaFullVersion, wow64 |
CRITICAL | C++ патч NavigatorUAData. Brands без HeadlessChrome. Синхронизация fullVersion с UA-строкой |
navigator.platform |
Соответствие с OS в UA. Linux x86_64 / Win32 / MacIntel |
HIGH | Зависит от профиля OS. Строгая связь с UA и userAgentData.platform |
navigator.hardwareConcurrency |
Количество ядер CPU. 0 или 1 — подозрительно | HIGH | Значения: 2, 4, 6, 8, 12, 16. Распределение по реальной статистике. Связано с deviceMemory |
navigator.deviceMemory |
Объём RAM в GB. Должен коррелировать с CPU | HIGH | Значения: 2, 4, 8. Корреляция: 2 ядра → 2-4 GB, 4+ ядер → 4-8 GB |
navigator.webdriver |
true = 100% бот |
CRITICAL | Патч на уровне C++: удаление флага из Blink. Не JS-override — проверяется через Object.getOwnPropertyDescriptor |
navigator.maxTouchPoints |
Desktop = 0, мобильные > 0 | MEDIUM | Desktop: 0. Мобильный профиль: 5 или 10 |
navigator.languages |
Массив языков. Должен совпадать с geo | HIGH | Для РФ: ["ru-RU","ru","en-US","en"] или ["ru"]. Привязка к IP-геолокации |
navigator.pdfViewerEnabled |
Наличие PDF-плагина | LOW | Для Chrome: true (стандартное значение) |
navigator.plugins |
Список плагинов. Пустой = подозрительно | HIGH | Стандартный набор Chrome: PDF Viewer, Chrome PDF Viewer, Chromium PDF Viewer, Native Client, Chrome PDF Plugin. 5 плагинов + соответствующие MIME-типы |
navigator.cookieEnabled |
Cookies включены | MEDIUM | Всегда true |
navigator.vendor |
"Google Inc." для Chrome |
MEDIUM | Фиксированное значение для Chrome-профиля |
navigator.productnavigator.productSub |
"Gecko" / "20030107" — исторические значения |
LOW | Фиксированные значения, не менять |
| Параметр | Что проверяет Яндекс | Критичность | Стратегия подмены |
|---|---|---|---|
screen.width/height |
Разрешение экрана. Нулевые значения = headless | CRITICAL | Из базы реальных разрешений: 1920x1080, 1366x768, 1440x900, 2560x1440, 1536x864. Распределение по статистике РФ |
screen.availWidth/Height |
Доступная область (минус панель задач) | HIGH | Windows: height - 40px (таскбар). Mac: height - 25px (меню). Linux: равно полному. Должно быть ≤ screen |
screen.colorDepthscreen.pixelDepth |
Глубина цвета. Всегда 24 на десктопе | MEDIUM | 24 для обоих значений. Всегда равны друг другу |
window.devicePixelRatio |
DPR. 1 = обычный, 2 = Retina/HiDPI | HIGH | Должен коррелировать с экраном. 4K → DPR 1.5-2. 1080p → DPR 1. Mac → DPR 2 |
window.innerWidth/Heightwindow.outerWidth/Height |
Размер viewport и окна. Outer > Inner | HIGH | Inner < availWidth. Outer = inner + хром браузера (~80-120px по высоте). Рандомизация в пределах реальных значений |
screen.orientation |
Тип ориентации и угол | LOW | Desktop: "landscape-primary", angle: 0 |
visualViewport |
Параметры визуального viewport | MEDIUM | width/height = innerWidth/Height. scale = 1. offsetLeft/Top = 0 |
| Параметр | Что проверяет Яндекс | Критичность | Стратегия подмены |
|---|---|---|---|
Canvas Fingerprint |
Hash отрисовки текста/графики. Headless даёт одинаковые результаты | CRITICAL | Noise injection на уровне Skia (C++ патч). Добавление шума к каждому пикселю при toDataURL(). Шум должен быть детерминированным (seed от профиля) — тот же профиль = тот же hash |
WebGL Vendor |
Производитель GPU. SwiftShader/Mesa = VM | CRITICAL | Реальные значения: "Intel Inc.", "NVIDIA Corporation", "ATI Technologies Inc.". Связано с Renderer |
WebGL Renderer |
Модель GPU. Должна соответствовать Vendor и OS | CRITICAL | База реальных GPU: "ANGLE (Intel, Intel(R) UHD Graphics 630, ...)". Строго привязано к OS/Vendor |
WebGL Extensions |
Количество и набор расширений | HIGH | 30-40 расширений для реального GPU. Набор зависит от GPU модели. Хранить в базе привязанными к конкретным renderer'ам |
WebGL Precision |
12 типов точности (vertex/fragment shaders) | HIGH | Зависит от GPU. Хранить связанными с renderer |
WebGL Version |
Строка версии WebGL | MEDIUM | "WebGL 1.0 (OpenGL ES 2.0 Chromium)" — стандартная для Chrome |
WebGL Shading |
Версия языка шейдеров | MEDIUM | "WebGL GLSL ES 1.0 (OpenGL ES GLSL ES 1.0 Chromium)" |
| Параметр | Что проверяет Яндекс | Критичность | Стратегия подмены |
|---|---|---|---|
AudioContextfingerprint |
Сумма сигнала осциллятора. Нулевой результат = VM/headless | CRITICAL | Noise injection в OfflineAudioContext. Сумма должна быть в диапазоне 35-124. Seed от профиля для стабильности |
Audio Buffer |
Длина буфера обработки | MEDIUM | Стандартное значение: 4096 или 5000 сэмплов |
| Параметр | Что проверяет Яндекс | Критичность | Стратегия подмены |
|---|---|---|---|
Font Detection |
Набор установленных шрифтов. Пустой = подозрительно | HIGH | Подмена через measureText() в Canvas API. Набор шрифтов привязан к OS. Windows: 50-100 шрифтов. Mac: 60-80. Linux: 20-40. Обязательные: Arial, Verdana, Times New Roman, Georgia, Courier New + OS-специфичные (Segoe UI для Win, SF для Mac) |
| Параметр | Что проверяет Яндекс | Критичность | Стратегия подмены |
|---|---|---|---|
Video Codecs |
Поддержка h264, vp8, vp9, av1, h265, theora | MEDIUM | Chrome стандарт: h264=probably, vp8=probably, vp9=probably, av1=probably, h265="", theora="" |
Speech Synthesis |
Набор голосов. Пустой = подозрительно | HIGH | Подмена speechSynthesis.getVoices(). Windows: 3-5 голосов (Microsoft David, Zira). Mac: 20+ голосов. Linux: 0-2 (eSpeak). Привязка к OS |
Media Devices |
Список устройств (камеры, микрофоны). Пустой = подозрительно | HIGH | Генерация 1-3 устройств: audioinput, videoinput, audiooutput. Device ID — стабильный hash от профиля |
Widevine DRM |
Поддержка Widevine. Зависит от платформы | MEDIUM | Chrome desktop: поддерживается. Headless: обычно нет |
| Параметр | Что проверяет Яндекс | Критичность | Стратегия подмены |
|---|---|---|---|
WebRTC IP Leak |
Утечка реального IP через STUN-запросы. Несоответствие IP прокси и WebRTC = бот | CRITICAL | 3 режима: (1) Полная блокировка WebRTC. (2) Подмена IP через кастомный TURN-сервер. (3) Подмена SDP candidates на IP прокси. Предпочтительно: режим 2 или 3 |
Network Information |
effectiveType, downlink, rtt, saveData |
MEDIUM | effectiveType: "4g". downlink: 5-10 Mbps (рандом). rtt: 50-150ms. saveData: false |
STUN Requests |
Запросы к STUN-серверам (Google). Детектируются IDS | HIGH | Подмена STUN-сервера или проксирование через собственный TURN |
| Параметр | Что проверяет Яндекс | Критичность | Стратегия подмены |
|---|---|---|---|
window.webdrivernavigator.webdriver |
Наличие = автоматизация | CRITICAL | Удаление на уровне C++ в Blink. Delete/override в JS — детектируется через property descriptor |
Phantom/Nightmaredetection |
window._phantom, window.__nightmare, window.callPhantom |
CRITICAL | Не должны существовать в window. Патч Chromium: не добавлять |
Selenium markers |
document.$cdc_, $chrome_asyncScriptInfo, __selenium_unwrapped |
CRITICAL | Полное удаление ChromeDriver-маркеров из исходников. Патч Runtime.enable |
Playwright markers |
__playwright, __pw_* |
CRITICAL | Не использовать Playwright. Собственная CDP-обёртка |
Puppeteer markers |
__puppeteer_evaluation_script__ |
CRITICAL | Не использовать Puppeteer. Или патчить runtime |
chrome object |
Наличие window.chrome и его свойств. Headless Chrome = отсутствует или неполный |
CRITICAL | Полная эмуляция объекта chrome с runtime, loadTimes(), csi(). Проверка в iframe |
Prototype checks |
HTMLElement.toString, Intl.DateTimeFormat.toString — выявляют подделку нативных функций |
HIGH | Все подменяемые функции должны возвращать "[object Function]" или "function ... { [native code] }" через toString(). Патч на C++ уровне |
eval.toString().length |
Длина строки eval.toString(). Разная для разных движков |
MEDIUM | Chrome: 33. Должно быть стабильным |
Error.stack |
Наличие и формат stack trace | MEDIUM | Должен быть доступен. Формат Chrome-specific |
| Параметр | Что проверяет Яндекс | Критичность | Стратегия подмены |
|---|---|---|---|
Intl.DateTimeFormattimezone |
Таймзона. Несоответствие с IP = красный флаг | CRITICAL | Автоматическая привязка к геолокации прокси. Москва → Europe/Moscow, Новосибирск → Asia/Novosibirsk и т.д. |
Date.getTimezoneOffset() |
Смещение в минутах. Должно совпадать с TZ | CRITICAL | Автоматический расчёт из timezone. Europe/Moscow → -180 |
DST detection |
Сравнение offset января и июля для определения DST | HIGH | Россия: DST=false (отменён в 2014). Jan offset = Jul offset. Важно для корректности |
Locale (Intl) |
Формат чисел, дат. Должен совпадать с language | HIGH | Для РФ: "ru-RU". Проверка через Intl.NumberFormat |
| Параметр | Что проверяет Яндекс | Критичность | Стратегия подмены |
|---|---|---|---|
Math precision |
Точность 16 математических функций (sin, cos, tan, acos, asin, atan, etc.) | HIGH | Зависит от JS-движка и версии V8. Одинаковые для всех Chrome одной версии. Не подменять — использовать реальные значения |
Float32 NaN byte |
Специфичное поведение NaN в Float32Array. Зависит от архитектуры CPU | MEDIUM | x86: byte[3] = 255 (или 127). ARM: может отличаться. Не менять для x86 |
Performance.now() |
Точность таймера. Слишком высокая = headless | HIGH | Ограничить точность до 0.1-1ms (стандартная для Chrome с Site Isolation). Min delta > 0 |
JS Heap Memory |
jsHeapSizeLimit, totalJSHeapSize, usedJSHeapSize. Нулевые = подозрительно |
MEDIUM | Limit: 2-4 GB. Total > Used. Реалистичные значения: 10-50 MB used |
Storage APIs |
localStorage, sessionStorage, indexedDB, cookies — все должны быть доступны | HIGH | Все = true. Блокировка = признак автоматизации |
Permissions API |
Статус 17-24 разрешений. У реальных пользователей — mix состояний | HIGH | Большинство = "prompt". 1-2 = "denied" (notifications). Никогда все "granted" |
Battery API |
Состояние батареи. Desktop = charging, level 1 | MEDIUM | Desktop: charging=true, level=1, chargingTime=0. Ноутбук: level 0.5-1.0, рандом |
CSS Media Queries |
prefers-color-scheme, prefers-reduced-motion, color-gamut | MEDIUM | Рандомный выбор light/dark. Motion: no-preference. Gamut: srgb (или p3 для Mac) |
AdBlock detection |
Наличие блокировщика рекламы по 4 спискам | MEDIUM | 30-40% профилей — с адблоком. Реалистичная пропорция. Установка uBlock Origin для части профилей |
Window objects |
Наличие RTCPeerConnection, Notification, DeviceMotionEvent, Intl_DateTimeFormat | MEDIUM | Все стандартные API Chrome должны присутствовать |
Notification API |
Permission status и constructor | MEDIUM | Permission: "default" или "denied". Constructor: нативный |
Document Rect |
Размеры корневого элемента через getBoundingClientRect | MEDIUM | Должны соответствовать innerWidth/Height |
Принцип: IP — это первое, что проверяется. Все остальные параметры профиля привязываются к геолокации IP. Несоответствие timezone/language/geo с IP — главный красный флаг.
| Тип | Trust Level | Описание | Применение |
|---|---|---|---|
| Резидентные (ISP) | Высокий | IP реальных провайдеров (Ростелеком, Билайн, МТС, Мегафон). ASN домашних пользователей | Основной тип. Привязка 1 IP → 1 профиль на длительный срок |
| Мобильные | Высокий | IP мобильных операторов. Естественная ротация IP (CGNAT) | Для мобильных профилей. Допускается смена IP в пределах оператора/региона |
| Серверные (DC) РФ | Средний | IP дата-центров. Легко определяются по ASN | Только для тестирования. Не для продакшена |
| Публичные | Низкий | Общедоступные прокси. Забанены в базах | Не использовать |
ProxyPool
├── providers/
│ ├── residential/ # Резидентные провайдеры
│ │ ├── sticky sessions # Закреплённые IP за профилем
│ │ └── geo targeting # Город/регион → конкретный exit node
│ └── mobile/ # Мобильные провайдеры
│ ├── rotation policy # Ротация по расписанию (каждые 4-8 часов)
│ └── operator match # IP оператора ↔ профиль устройства
├── geo_resolver/
│ ├── ip → city # MaxMind GeoIP2 City
│ ├── city → timezone # Mapping города → таймзона
│ ├── city → language # Mapping города → предпочитаемый язык
│ └── asn → isp_name # ASN → имя провайдера (для валидации)
└── health_check/
├── latency monitor # Проверка задержки каждые 5 мин
├── ban detection # Мониторинг капчи/блокировок
└── ip reputation # Проверка IP в базах спам-листов
Яндекс анализирует поведение через watch_serp.js (108 KB, обновляется часто — 500 версий в архиве sosiska.top). Скрипт трекает: движения мыши, скролл, клики, время на странице, переходы, возвраты в выдачу.
Движение мыши — основной сигнал. Прямые линии и телепортации = бот.
Каждый параметр связан с другими. Изолированная подмена одного значения без обновления зависимых — главная причина детекции.
| Связка | Правило | Пример нарушения |
|---|---|---|
UA ↔ userAgentData |
Версия Chrome в UA-строке = uaFullVersion. Brands не содержат HeadlessChrome | UA: Chrome/150, brands: Chrome/148 |
UA ↔ platform |
OS в UA совпадает с navigator.platform и userAgentData.platform | UA: Windows NT 10.0, platform: Linux x86_64 |
GPU ↔ Canvas hash |
Один и тот же GPU vendor/renderer даёт стабильный canvas. Разные GPU = разный hash | Intel GPU + canvas hash от NVIDIA |
GPU ↔ WebGL extensions |
Набор расширений зависит от GPU модели | SwiftShader GPU + 40 расширений |
OS ↔ Fonts |
Windows имеет Segoe UI, Mac — SF Pro, Linux — минимальный набор | Linux platform + 80 Windows-шрифтов |
OS ↔ Voices |
speechSynthesis голоса зависят от OS | Linux + голоса Microsoft David |
IP geo ↔ Timezone |
Строгое соответствие города IP и таймзоны | IP: Москва, TZ: Asia/Bangkok |
IP geo ↔ Language |
Язык должен соответствовать региону | IP: Россия, language: zh-CN |
Screen ↔ DPR ↔ Viewport |
innerWidth ≤ availWidth ≤ screen.width. DPR согласован | innerWidth > screen.width |
CPU ↔ Memory |
hardwareConcurrency коррелирует с deviceMemory | 16 ядер + 2GB RAM |
Desktop ↔ Touch |
Десктоп: maxTouchPoints=0, нет touch events. Мобильный: >0 | Desktop UA + maxTouchPoints=5 |
WebRTC ↔ Proxy IP |
IP через WebRTC = IP прокси | Proxy: 195.x.x.x, WebRTC leak: 92.x.x.x |
DST ↔ Region |
Россия: DST=false с 2014. Jan offset = Jul offset | TZ: Europe/Moscow, но DST=true |
Каждый профиль перед использованием проходит проверку. Целевой Trust Score: ≥ 85/100. Система воспроизводит логику tmgrdfrend.fp.js.
| Группа | Вес | Ключевые проверки |
|---|---|---|
| Automation Detection | 25% | webdriver=false, отсутствие маркеров Selenium/Playwright/Puppeteer, chrome object полный |
| Canvas + WebGL + Audio | 20% | Уникальные хеши, реальный GPU vendor/renderer, audio сумма > 0 |
| Navigator Consistency | 15% | UA ↔ userAgentData, plugins не пустые, platform ↔ OS |
| Geo/TZ/Language | 15% | IP ↔ timezone ↔ language ↔ locale. DST корректность |
| Screen/Viewport | 10% | Реалистичные размеры, inner < avail < screen, DPR корреляция |
| Media/Fonts/Voices | 10% | Шрифты ≥ 10, голоса > 0, media devices > 0 |
| Network/WebRTC | 5% | Нет IP leak, Network API реалистичный |
validate(profile) {
score = 100
// CRITICAL checks (fail = score 0)
if (profile.webdriver === true) → return 0
if (profile.ua contains "HeadlessChrome") → return 0
if (profile.brands contains "Headless") → return 0
if (profile.automationMarkers.length > 0) → return 0
// WEIGHTED checks
if (!matchUAvsUAData(profile)) → score -= 15
if (!matchIPvsTimezone(profile)) → score -= 15
if (profile.plugins.length === 0) → score -= 10
if (profile.fonts.length === 0) → score -= 10
if (profile.voices.length === 0) → score -= 5
if (profile.mediaDevices === 0) → score -= 5
if (profile.webrtcIP !== proxyIP) → score -= 10
if (!matchOSvsFonts(profile)) → score -= 8
if (!matchGPUvsCanvas(profile)) → score -= 10
if (profile.audioSum === 0) → score -= 10
if (!matchScreenConsistency(prof)) → score -= 5
if (profile.batteryLevel === 0) → score -= 3
if (profile.performancePrecision
< 0.01) → score -= 5
return max(0, score)
}
selectProxy(requirements) {
proxy = pool.getSticky(requirements.region, requirements.isp)
geo = geoResolver.resolve(proxy.ip)
return {
ip: proxy.ip,
port: proxy.port,
protocol: "socks5",
city: geo.city, // "Москва"
region: geo.region, // "Московская область"
timezone: geo.timezone, // "Europe/Moscow"
offset: geo.offset, // -180
isp: geo.isp, // "Rostelecom"
asn: geo.asn, // AS12389
language: geo.language, // "ru-RU"
}
}
generateHardware(os) {
// Выбор из базы реальных комбинаций
snapshot = hardwareDB.sample(os) // os: "Windows" | "Mac" | "Linux"
return {
cpu: snapshot.cores, // 4, 8, 12, 16
memory: snapshot.ram, // 4, 8 (GB)
gpu: {
vendor: snapshot.gpuVendor, // "Intel Inc."
renderer: snapshot.gpuRenderer, // "ANGLE (Intel, UHD 630, ...)"
extensions: snapshot.glExtensions, // 35 extensions
precision: snapshot.glPrecision, // 12 types
},
screen: {
width: snapshot.screenW, // 1920
height: snapshot.screenH, // 1080
dpr: snapshot.dpr, // 1
colorDepth: 24,
},
}
}
generateBrowserProfile(proxy, hardware, os) {
chromeVersion = getCurrentStableVersion() - random(0, 2)
return {
userAgent: buildUA(os, chromeVersion),
userAgentData: buildUAData(os, chromeVersion),
platform: platformForOS(os),
plugins: standardChromePlugins(),
fonts: fontsForOS(os), // 20-80 шрифтов
voices: voicesForOS(os), // 0-20 голосов
mediaDevices: generateDevices(1, 3), // 1-3 устройства
canvas: { seed: randomSeed() }, // для noise injection
audio: { seed: randomSeed() },
webrtc: { mode: "proxy_ip", turnServer: "turn.our.server" },
timezone: proxy.timezone,
language: proxy.language,
languages: [proxy.language, "en-US", "en"],
locale: proxy.language,
permissions: generatePermissions(), // mix prompt/denied
battery: { charging: true, level: 1 },
network: { effectiveType: "4g", downlink: random(5,10) },
colorScheme: random(["light", "dark"]),
adblock: random() < 0.35, // 35% с адблоком
}
}
Новый профиль без истории и cookies — подозрителен. Перед использованием в целевых задачах:
yandexuid, i, yp, ys — все должны накопиться естественноНе все профили одинаково полезны. Авторизованный профиль в Яндексе весит в 3-10 раз больше анонимного для ранжирования. Система поддерживает три класса с разным уровнем доверия и ценой создания.
| Параметр | Anonymous | Lite (рекомендуемый) | Full Account |
|---|---|---|---|
| Идентификация | Только cookies (yandexuid) | Яндекс ID через внешнюю почту | Яндекс ID + @yandex.ru + телефон |
| Вес сигнала | 1× | 3-5× | 5-10× |
| Требования | Ничего | Email (Gmail и т.д.) | Email + телефон + логин |
| Яндекс Почта | — | — | Есть (@yandex.ru) |
| Яндекс Диск | — | — | Есть |
| Отзывы/Карты | — | Можно оставлять | Можно оставлять |
| Поиск: история | Только в cookies | Привязана к ID | Привязана к ID |
| Верификация | — | — | Опционально (Mobile ID/ЕСИА) |
| Профили Яндекс ID | — | Один профиль | Объединение до 10 по телефону |
| Стоимость создания | Бесплатно | ~$0 (нужен только email) | $0.05-0.30 (SMS-верификация) |
| Сложность автоматизации | Тривиально | Низкая | Средняя (нужен SMS-сервис) |
| Риск бана | Легко пережить | Терпимо | Дорого |
| Доля в пуле | 40-50% | 40-50% | 5-15% |
Яндекс поддерживает упрощённую регистрацию с внешней почтой. Это создаёт полноценный Яндекс ID, который распознаётся на поиске.
createLiteAccount(persona) {
email = emailPool.get() // Gmail/Outlook/Proton
// 1. Открыть любой сервис Яндекса
navigate("https://yandex.ru")
click("Войти")
// 2. Ввести внешний email как логин
fill(loginField, email) // user+profile42@gmail.com
click("Войти")
// 3. Получить код подтверждения
code = emailPool.fetchCode(email) // код из письма
fill(codeField, code)
// 4. Указать имя и фамилию персоны
fill(firstNameField, persona.firstName) // "Алексей"
fill(lastNameField, persona.lastName) // "Петров"
click("Далее")
// 5. Создать пароль
fill(passwordField, generatePassword())
click("Далее")
// 6. Принять соглашение
click("Зарегистрироваться")
// Результат: активный Яндекс ID
// Логин для входа: внешний email
// Доступ: расширенные возможности сервисов
// НЕТ: @yandex.ru, Диск
}
user+tag@gmail.com — каждый alias получает почту на основной ящик. Один Gmail = практически неограниченное количество Lite-аккаунтовЯндекс обновляет скрипты постоянно: watch_serp.js меняется десятки раз в день (500 версий за месяцы). tmgrdfrend.fp.js — реже, но каждое обновление может добавить новые проверки. Необходим постоянный мониторинг.
tmgrdfrend.fp.js каждую минутуwatch_serp.js каждую минуту| Компонент | Технология | Обоснование |
|---|---|---|
| Browser Engine | Chromium (собственная сборка) | Полный контроль. Патчи на уровне C++ для Canvas, WebGL, Audio, Navigator, WebRTC |
| Profile Storage | SQLite + encrypted blobs | Каждый профиль = отдельный файл. Cookies, localStorage, history, cache отдельно |
| Profile Generator | Node.js / Python | Генерация JSON-конфигов из базы реальных снапшотов. Валидация консистентности |
| Hardware DB | PostgreSQL | База реальных комбинаций: GPU + Screen + Fonts + Voices. Регулярное пополнение |
| Behavior Engine | TypeScript (в браузере) | Mouse curves, scroll patterns, typing simulation. Выполняется внутри Chromium |
| Proxy Manager | Go / Rust | Высокая производительность для управления пулом. Health checks, rotation, sticky sessions |
| Script Monitor | Node.js + cron | Мониторинг изменений tmgrdfrend.fp.js и watch_serp.js. Diff + алерты |
| Orchestrator | Python / Node.js | Управление множеством профилей. Расписание сессий, балансировка нагрузки |
| API | REST + WebSocket | Управление профилями, статус сессий, мониторинг в реальном времени |
yanprofile/
├── chromium-patches/ # Патчи для Chromium
│ ├── navigator/ # webdriver, UA, platform, plugins
│ ├── canvas/ # noise injection в Skia
│ ├── webgl/ # vendor, renderer, extensions
│ ├── audio/ # AudioContext fingerprint
│ ├── webrtc/ # IP leak prevention
│ └── misc/ # performance.now, battery, etc.
├── profile-generator/ # Генератор профилей
│ ├── hardware-db/ # База реальных устройств
│ ├── generators/ # Генераторы для каждой группы параметров
│ ├── validators/ # Проверки консистентности
│ └── templates/ # Шаблоны OS-специфичных профилей
├── behavior-engine/ # Поведенческий движок
│ ├── mouse/ # Движение мыши (Безье + шум)
│ ├── scroll/ # Паттерны скролла
│ ├── typing/ # Набор текста с ошибками
│ ├── scenarios/ # Готовые сценарии (SERP, сайт, etc.)
│ └── timing/ # Распределение времени и пауз
├── proxy-manager/ # Управление прокси
│ ├── pool/ # Пул прокси с health checks
│ ├── geo-resolver/ # IP → город → timezone → language
│ ├── webrtc-turn/ # TURN-сервер для WebRTC
│ └── rotation/ # Политики ротации
├── script-monitor/ # Мониторинг скриптов Яндекса
│ ├── watchers/ # tmgrdfrend.fp.js, watch_serp.js
│ ├── diff-engine/ # Сравнение версий
│ └── alerts/ # Уведомления об изменениях
├── orchestrator/ # Оркестратор сессий
│ ├── scheduler/ # Расписание запусков
│ ├── warmup/ # Прогрев профилей
│ └── monitor/ # Мониторинг здоровья
└── api/ # REST API
├── profiles/ # CRUD профилей
├── sessions/ # Управление сессиями
└── dashboard/ # UI для мониторинга
| Фаза | Задачи | Результат |
|---|---|---|
| Phase 1 |
|
MVP: 1 профиль проходит проверку pager-syzran.ru с Trust Score ≥ 80 |
| Phase 2 |
|
Trust Score ≥ 90. Прохождение SmartCaptcha < 5% |
| Phase 3 |
|
Полный цикл: генерация → прогрев → работа |
| Phase 4 |
|
Продакшен-система с автообновлением |