Назад к блогу

Как избежать детекции автоматизации через timing attacks: защита в Selenium и Puppeteer

Timing attacks — один из самых сложных методов детекции автоматизации. Разбираем как защититься от анализа времени выполнения действий в браузере.

📅10 января 2026 г.

Timing attacks — это метод детекции ботов, основанный на анализе времени выполнения действий в браузере. Современные антифрод-системы Facebook, Google, TikTok и других платформ анализируют не только что вы делаете, но и как быстро. Слишком быстрые клики, мгновенная загрузка страниц, отсутствие естественных пауз — всё это выдаёт автоматизацию. В этой статье разберём технические методы защиты от timing attacks для разработчиков, работающих с Selenium, Puppeteer и антидетект-браузерами.

Что такое timing attacks и как они работают

Timing attack — это метод детекции автоматизации, основанный на измерении временных интервалов между действиями пользователя. Антифрод-системы собирают телеметрию: сколько времени прошло от загрузки страницы до первого клика, как быстро пользователь скроллит, есть ли паузы при заполнении форм. Эти данные сравниваются с поведенческими моделями реальных людей.

Основные временные метрики, которые анализируют системы защиты:

  • Time to First Interaction (TTFI) — время от загрузки страницы до первого действия (клик, скролл, ввод текста). Боты обычно начинают действовать мгновенно после загрузки DOM, человек — через 0.5-3 секунды.
  • Click timing patterns — интервалы между кликами. Автоматизированные скрипты часто кликают с одинаковой частотой (например, ровно каждые 2 секунды), человек — хаотично.
  • Typing speed consistency — скорость набора текста. Боты вводят текст мгновенно или с постоянной задержкой между символами, человек — с вариативной скоростью и паузами.
  • Mouse movement velocity — скорость движения курсора. Selenium по умолчанию мгновенно телепортирует курсор в нужную точку, человек двигает мышь с ускорением и замедлением.
  • Scroll behavior — паттерны прокрутки страницы. Боты часто скроллят ровно на фиксированное количество пикселей, человек — неравномерно, с остановками.

Системы детекции используют машинное обучение для анализа этих метрик. Они строят профиль поведения и вычисляют вероятность того, что пользователь — бот. Если временные паттерны слишком идеальные или слишком быстрые — это красный флаг.

Важно: Timing attacks особенно эффективны против массовой автоматизации. Если вы запускаете 100 браузеров с одинаковыми временными паттернами, антифрод-система легко их обнаружит по статистическим аномалиям.

Методы детекции автоматизации через временные паттерны

Современные антифрод-системы используют несколько слоёв анализа временных характеристик. Рассмотрим конкретные техники, которые применяют Facebook, Google, Cloudflare и другие платформы.

1. Анализ Performance API

Браузеры предоставляют Performance API, который собирает детальную телеметрию загрузки страницы. Антифрод-системы анализируют:

// Пример данных Performance API
performance.timing = {
  navigationStart: 1234567890000,
  domLoading: 1234567890150,      // +150ms
  domInteractive: 1234567890300,  // +300ms
  domComplete: 1234567890500,     // +500ms
  loadEventEnd: 1234567890600     // +600ms
}

// Подозрительные паттерны для ботов:
// - Слишком быстрая загрузка (domComplete < 200ms)
// - Идеально ровные интервалы между событиями
// - Отсутствие задержек на загрузку внешних ресурсов

Headless-браузеры (особенно старые версии Puppeteer и Selenium) часто показывают аномально быстрые значения navigationStart и domLoading, потому что не загружают изображения, шрифты и другие ресурсы так же, как обычный браузер.

2. Event Timing Analysis

JavaScript-трекеры отслеживают временные метки всех событий (клики, движения мыши, нажатия клавиш) и анализируют паттерны:

// Пример сбора телеметрии событий
const events = [];

document.addEventListener('click', (e) => {
  events.push({
    type: 'click',
    timestamp: performance.now(),
    x: e.clientX,
    y: e.clientY
  });
});

// Анализ подозрительных паттернов:
// - Клики происходят ровно каждые N миллисекунд
// - Нет микродвижений мыши перед кликом
// - Первый клик происходит мгновенно после загрузки страницы

3. Keystroke Dynamics

При заполнении форм антифрод-системы анализируют динамику нажатий клавиш — уникальный биометрический показатель каждого человека:

  • Dwell time — время удержания клавиши (от keydown до keyup). У человека варьируется от 50 до 200 мс, у ботов — константа.
  • Flight time — время между отпусканием одной клавиши и нажатием следующей. У человека — от 100 до 500 мс с вариациями, у ботов — фиксированная задержка.
  • Typing rhythm — общий ритм набора. Человек делает паузы на знаках препинания, исправляет ошибки, боты — нет.

Пример детекции: Если вы используете element.send_keys("текст") в Selenium, весь текст вводится за 1-2 миллисекунды — это мгновенно выдаёт автоматизацию.

Имитация человеческих задержек в коде

Первый уровень защиты от timing attacks — добавление задержек между действиями. Но важно не просто вставить time.sleep(2), а имитировать естественное поведение.

Базовая имитация задержек в Python (Selenium)

import time
import random
from selenium import webdriver
from selenium.webdriver.common.by import By

def human_delay(min_sec=0.5, max_sec=2.0):
    """Случайная задержка, имитирующая человека"""
    delay = random.uniform(min_sec, max_sec)
    time.sleep(delay)

driver = webdriver.Chrome()
driver.get("https://example.com")

# Задержка перед первым действием (человек читает страницу)
human_delay(1.5, 4.0)

# Клик по элементу
button = driver.find_element(By.ID, "submit-btn")
button.click()

# Задержка перед следующим действием
human_delay(0.8, 2.5)

Продвинутая имитация с нормальным распределением

Равномерное распределение (uniform) выглядит неестественно. Человеческие задержки подчиняются нормальному распределению с выбросами:

import numpy as np

def realistic_delay(mean=1.5, std_dev=0.5, min_val=0.3, max_val=5.0):
    """
    Задержка с нормальным распределением
    mean: среднее время задержки
    std_dev: стандартное отклонение
    min_val, max_val: границы (чтобы избежать экстремальных значений)
    """
    delay = np.random.normal(mean, std_dev)
    delay = max(min_val, min(max_val, delay))  # Ограничиваем диапазон
    time.sleep(delay)
    return delay

# Использование
realistic_delay(mean=2.0, std_dev=0.7)  # Среднее 2 сек, но с вариациями

Контекстные задержки

Разные действия требуют разного времени. Создайте профили задержек для разных сценариев:

class HumanBehavior:
    """Профили задержек для разных типов действий"""
    
    @staticmethod
    def page_load_delay():
        """Задержка после загрузки страницы (чтение контента)"""
        return realistic_delay(mean=2.5, std_dev=1.0, min_val=1.0, max_val=6.0)
    
    @staticmethod
    def before_click():
        """Задержка перед кликом (поиск элемента глазами)"""
        return realistic_delay(mean=0.8, std_dev=0.3, min_val=0.3, max_val=2.0)
    
    @staticmethod
    def before_typing():
        """Задержка перед началом ввода текста"""
        return realistic_delay(mean=1.2, std_dev=0.5, min_val=0.5, max_val=3.0)
    
    @staticmethod
    def between_form_fields():
        """Задержка между полями формы"""
        return realistic_delay(mean=0.6, std_dev=0.2, min_val=0.2, max_val=1.5)

# Использование в скрипте
driver.get("https://example.com/login")
HumanBehavior.page_load_delay()

username_field = driver.find_element(By.ID, "username")
HumanBehavior.before_typing()
# ... ввод логина ...

HumanBehavior.between_form_fields()

password_field = driver.find_element(By.ID, "password")
HumanBehavior.before_typing()
# ... ввод пароля ...

Рандомизация времени выполнения действий

Одинаковые задержки между действиями — это статистическая аномалия. Если вы запускаете 100 экземпляров скрипта, и все они кликают на кнопку ровно через 2.5 секунды после загрузки — это легко детектируется. Нужна рандомизация на нескольких уровнях.

1. Рандомизация порядка действий

Добавьте вариативность в последовательность действий. Например, перед заполнением формы иногда скроллите страницу, иногда — нет:

def fill_form_naturally(driver):
    # 30% вероятность проскроллить страницу перед заполнением
    if random.random() < 0.3:
        driver.execute_script("window.scrollBy(0, 200)")
        human_delay(0.5, 1.5)
    
    # 20% вероятность кликнуть в случайное место (имитация чтения)
    if random.random() < 0.2:
        body = driver.find_element(By.TAG_NAME, "body")
        body.click()
        human_delay(0.3, 0.8)
    
    # Основное действие — заполнение формы
    username_field = driver.find_element(By.ID, "username")
    type_like_human(username_field, "myusername")

2. Вариативная скорость набора текста

Вместо мгновенного ввода текста имитируйте посимвольный набор с вариативной скоростью:

def type_like_human(element, text):
    """Ввод текста с имитацией человеческой скорости набора"""
    for char in text:
        element.send_keys(char)
        
        # Базовая задержка между символами
        base_delay = random.uniform(0.05, 0.15)
        
        # Дополнительные паузы на пробелах и знаках препинания
        if char in [' ', '.', ',', '!', '?']:
            base_delay += random.uniform(0.1, 0.3)
        
        # Случайные "задумчивости" (5% вероятность длинной паузы)
        if random.random() < 0.05:
            base_delay += random.uniform(0.5, 1.5)
        
        time.sleep(base_delay)
    
    # Иногда делаем опечатку и исправляем (10% вероятность)
    if random.random() < 0.1:
        time.sleep(random.uniform(0.2, 0.5))
        element.send_keys(Keys.BACKSPACE)
        time.sleep(random.uniform(0.1, 0.3))
        element.send_keys(text[-1])  # Вводим последний символ заново

3. Puppeteer: медленный ввод текста

В Puppeteer есть встроенная опция delay для метода type(), но её нужно рандомизировать:

// Базовое использование (НЕ рекомендуется — фиксированная задержка)
await page.type('#username', 'myusername', { delay: 100 });

// Правильный подход — рандомизация для каждого символа
async function typeWithVariableSpeed(page, selector, text) {
  await page.click(selector);
  
  for (const char of text) {
    await page.keyboard.type(char);
    
    // Случайная задержка от 50 до 150 мс
    let delay = Math.random() * 100 + 50;
    
    // Дополнительная пауза на пробелах
    if (char === ' ') {
      delay += Math.random() * 200 + 100;
    }
    
    // Случайные длинные паузы (5% вероятность)
    if (Math.random() < 0.05) {
      delay += Math.random() * 1000 + 500;
    }
    
    await page.waitForTimeout(delay);
  }
}

// Использование
await typeWithVariableSpeed(page, '#username', 'myusername');

Естественное движение мыши и скорость прокрутки

Selenium и Puppeteer по умолчанию не двигают курсор мыши — они мгновенно телепортируют его в нужную точку и кликают. Это один из самых очевидных признаков автоматизации. Для имитации человеческого движения мыши нужны специальные библиотеки.

Библиотека pyautogui для плавного движения мыши

Библиотека pyautogui позволяет двигать курсор по кривой Безье с ускорением и замедлением:

import pyautogui
from selenium.webdriver.common.action_chains import ActionChains

def move_mouse_naturally(driver, element):
    """Плавное движение мыши к элементу с имитацией человека"""
    # Получаем координаты элемента
    location = element.location
    size = element.size
    
    # Целевая точка (центр элемента + небольшая случайность)
    target_x = location['x'] + size['width'] / 2 + random.randint(-5, 5)
    target_y = location['y'] + size['height'] / 2 + random.randint(-5, 5)
    
    # Плавное движение с переменной скоростью
    # duration — время движения (0.5-1.5 сек для реалистичности)
    duration = random.uniform(0.5, 1.5)
    
    # tweening — функция ускорения (easeInOutQuad имитирует человеческое движение)
    pyautogui.moveTo(target_x, target_y, duration=duration, tween=pyautogui.easeInOutQuad)
    
    # Небольшая пауза перед кликом (человек не кликает мгновенно)
    time.sleep(random.uniform(0.05, 0.15))
    
    # Клик
    element.click()

Важно: Этот метод работает только если Selenium управляет реальным окном браузера (не headless). Для headless-режима движение мыши бесполезно, так как антифрод-системы не видят курсор.

Puppeteer: имитация движения мыши

В Puppeteer можно использовать библиотеку ghost-cursor для реалистичного движения курсора:

// Установка: npm install ghost-cursor
const { createCursor } = require("ghost-cursor");
const puppeteer = require("puppeteer");

(async () => {
  const browser = await puppeteer.launch({ headless: false });
  const page = await browser.newPage();
  const cursor = createCursor(page);
  
  await page.goto("https://example.com");
  
  // Плавное движение к элементу и клик
  const button = await page.$("#submit-btn");
  await cursor.click(button);  // Курсор двигается по кривой Безье!
  
  // Альтернативно — движение к координатам
  await cursor.move("#username");  // Просто двигаем курсор без клика
  await page.waitForTimeout(300);
  await cursor.click();  // Кликаем в текущей позиции
})();

Имитация скроллинга

Человек не скроллит страницу ровно на 500 пикселей за раз. Скроллинг должен быть неравномерным, с паузами и иногда прокруткой назад:

def scroll_like_human(driver, target_position=None):
    """
    Имитация человеческого скроллинга
    target_position: целевая позиция в пикселях (если None — скроллим до конца)
    """
    current_position = driver.execute_script("return window.pageYOffset;")
    
    if target_position is None:
        # Скроллим до конца страницы
        target_position = driver.execute_script("return document.body.scrollHeight;")
    
    while current_position < target_position:
        # Случайный шаг скролла (100-400 пикселей)
        scroll_step = random.randint(100, 400)
        current_position += scroll_step
        
        # Скроллим
        driver.execute_script(f"window.scrollTo(0, {current_position});")
        
        # Пауза между скроллами (человек читает контент)
        time.sleep(random.uniform(0.3, 1.2))
        
        # Иногда скроллим немного назад (10% вероятность)
        if random.random() < 0.1:
            back_scroll = random.randint(50, 150)
            current_position -= back_scroll
            driver.execute_script(f"window.scrollTo(0, {current_position});")
            time.sleep(random.uniform(0.2, 0.6))

# Использование
scroll_like_human(driver, target_position=2000)  # Скроллим до 2000px

Время загрузки страниц и AJAX-запросов

Антифрод-системы анализируют не только действия пользователя, но и характеристики загрузки страниц. Headless-браузеры часто загружаются аномально быстро, потому что не загружают изображения, не выполняют некоторые скрипты, не рендерят CSS.

Проблема: слишком быстрая загрузка

Сравните типичные значения Performance API для обычного браузера и headless:

Метрика Обычный браузер Headless (подозрительно)
domContentLoaded 800-2000 мс 50-200 мс
loadEventEnd 2000-5000 мс 100-500 мс
Количество загруженных ресурсов 50-200 (изображения, CSS, JS) 5-20 (только критичные)

Решение: принудительная задержка загрузки

Добавьте искусственную задержку после загрузки страницы, чтобы метрики выглядели естественнее:

def load_page_naturally(driver, url):
    """Загрузка страницы с имитацией естественного времени загрузки"""
    start_time = time.time()
    driver.get(url)
    
    # Ждём полной загрузки DOM
    WebDriverWait(driver, 10).until(
        lambda d: d.execute_script("return document.readyState") == "complete"
    )
    
    # Вычисляем реальное время загрузки
    actual_load_time = time.time() - start_time
    
    # Если загрузка была слишком быстрой (< 1 сек), добавляем задержку
    if actual_load_time < 1.0:
        additional_delay = random.uniform(1.0, 2.5) - actual_load_time
        time.sleep(additional_delay)
    
    # Дополнительная задержка на "чтение страницы"
    time.sleep(random.uniform(0.5, 2.0))

# Использование
load_page_naturally(driver, "https://example.com")

Ожидание AJAX-запросов

Современные сайты загружают контент асинхронно через AJAX. Если ваш скрипт начинает действовать до завершения всех AJAX-запросов — это подозрительно. Используйте явные ожидания:

from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

def wait_for_ajax(driver, timeout=10):
    """Ожидание завершения всех AJAX-запросов (jQuery)"""
    WebDriverWait(driver, timeout).until(
        lambda d: d.execute_script("return jQuery.active == 0")
    )

# Для сайтов без jQuery — ожидание конкретного элемента
def wait_for_dynamic_content(driver, selector, timeout=10):
    """Ожидание появления динамически загруженного элемента"""
    WebDriverWait(driver, timeout).until(
        EC.presence_of_element_located((By.CSS_SELECTOR, selector))
    )
    # Дополнительная задержка после появления элемента
    time.sleep(random.uniform(0.3, 0.8))

Настройка защиты в антидетект-браузерах

Если вы работаете с арбитражем, мультиаккаунтингом или другими задачами, где критична анонимность, используйте антидетект-браузеры: Dolphin Anty, AdsPower, Multilogin, GoLogin. Они имеют встроенные механизмы защиты от timing attacks, но их нужно правильно настроить.

Dolphin Anty: настройка Human Typing

В Dolphin Anty есть функция "Human Typing" — автоматическая имитация человеческого набора текста. Настройка:

  • Откройте профиль браузера → вкладка "Automation"
  • Включите "Human Typing Emulation"
  • Настройте параметры:
    • Average typing speed: 150-250 символов/минуту (реалистичная скорость)
    • Variation: 30-50% (разброс скорости между символами)
    • Pause on punctuation: включено (паузы на знаках препинания)
    • Random typos: 2-5% (случайные опечатки с исправлением)

После этого любой ввод текста через API Dolphin будет автоматически имитировать человека.

AdsPower: настройка Mouse Movement

AdsPower позволяет записать паттерны движения мыши реального пользователя и воспроизводить их:

  1. Откройте профиль → "Advanced Settings" → "Mouse Behavior"
  2. Выберите режим "Record Real User":
    • Откройте браузер в обычном режиме
    • Выполните типичные действия (клики, скроллинг, движения мыши)
    • AdsPower запишет траектории движений
  3. При автоматизации через API AdsPower будет воспроизводить записанные паттерны с вариациями

Multilogin: Canvas Noise и WebGL Timing

Multilogin добавляет шум (noise) в Canvas и WebGL fingerprints, что влияет на timing attacks связанные с рендерингом:

  • Профиль → "Fingerprint settings" → "Canvas"
  • Включите "Canvas Noise" (добавляет микрозадержки в рендеринг Canvas)
  • Включите "WebGL Metadata Masking" (маскирует характеристики GPU, влияющие на скорость рендеринга)

Это защищает от детекции через анализ времени выполнения Canvas.toDataURL() и WebGL-операций.

Рекомендация: Если вы работаете с мультиаккаунтингом на Facebook, Instagram или TikTok, обязательно используйте антидетект-браузеры в связке с качественными резидентными прокси — это минимизирует риск chain-банов и детекции по IP.

Продвинутые техники обхода timing detection

Для особо защищённых платформ (Google, Facebook, банковские сайты) базовых методов может быть недостаточно. Рассмотрим продвинутые техники.

1. Подмена Performance API

Можно переопределить методы Performance API, чтобы возвращать реалистичные значения вместо реальных:

// Инъекция скрипта для подмены performance.now()
const script = `
  (function() {
    const originalNow = performance.now.bind(performance);
    let offset = 0;
    let lastValue = 0;
    
    performance.now = function() {
      const realValue = originalNow();
      // Добавляем случайный шум к времени
      const noise = Math.random() * 2 - 1; // от -1 до +1 мс
      let fakeValue = realValue + offset + noise;
      
      // Гарантируем монотонность (время не идёт назад)
      if (fakeValue <= lastValue) {
        fakeValue = lastValue + 0.1;
      }
      
      lastValue = fakeValue;
      return fakeValue;
    };
  })();
`;

// Puppeteer: инъекция при создании страницы
await page.evaluateOnNewDocument(script);

Внимание: Этот метод может быть обнаружен через проверку целостности нативных методов. Используйте только на сайтах без сильной защиты.

2. Throttling CPU и Network

Chrome DevTools Protocol позволяет искусственно замедлить CPU и сеть, чтобы метрики загрузки выглядели естественнее:

// Puppeteer: замедление CPU в 2 раза
const client = await page.target().createCDPSession();
await client.send('Emulation.setCPUThrottlingRate', { rate: 2 });

// Замедление сети (эмуляция 3G)
await page.emulateNetworkConditions({
  offline: false,
  downloadThroughput: 1.5 * 1024 * 1024 / 8, // 1.5 Mbps
  uploadThroughput: 750 * 1024 / 8,          // 750 Kbps
  latency: 40                                 // 40ms задержка
});

Это увеличит время загрузки страниц и выполнения JavaScript, делая профиль более похожим на реального пользователя со средней скоростью интернета.

3. Имитация фоновой активности

Реальные пользователи не сидят на одной вкладке — они переключаются между вкладками, сворачивают окна, отвлекаются. Имитируйте это:

async function simulateTabSwitch(page) {
  // Эмулируем переключение на другую вкладку (Page Visibility API)
  await page.evaluate(() => {
    Object.defineProperty(document, 'hidden', {
      get: () => true,
      configurable: true
    });
    
    document.dispatchEvent(new Event('visibilitychange'));
  });
  
  // Пауза (пользователь смотрит другую вкладку)
  await page.waitForTimeout(Math.random() * 3000 + 2000);
  
  // Возвращаемся на вкладку
  await page.evaluate(() => {
    Object.defineProperty(document, 'hidden', {
      get: () => false,
      configurable: true
    });
    
    document.dispatchEvent(new Event('visibilitychange'));
  });
}

// Использование: случайно "отвлекаемся" во время работы
if (Math.random() < 0.15) {  // 15% вероятность
  await simulateTabSwitch(page);
}

4. Использование реальных User Timing marks

Некоторые сайты создают собственные метки времени через User Timing API. Добавьте реалистичные метки:

// Создание реалистичных timing marks
await page.evaluate(() => {
  // Имитация "думания" пользователя перед действием
  performance.mark('user-started-reading');
  
  setTimeout(() => {
    performance.mark('user-found-button');
    performance.measure('reading-time', 'user-started-reading', 'user-found-button');
  }, Math.random() * 2000 + 1000);
});

5. Ротация прокси для снижения статистической корреляции

Даже если каждый экземпляр вашего скрипта имеет рандомизированные задержки, антифрод-системы могут обнаружить корреляцию, если все запросы идут с одного IP. Используйте ротацию прокси:

Ротация IP снижает вероятность того, что антифрод-система сможет собрать достаточно данных для статистического анализа ваших timing паттернов.

Заключение

Timing attacks — это сложный метод детекции автоматизации, который требует комплексного подхода к защите. Основные выводы:

  • Не используйте фиксированные задержки — всегда рандомизируйте время между действиями с использованием нормального распределения
  • Имитируйте контекстные задержки — разные действия требуют разного времени (чтение страницы ≠ клик по кнопке)
  • Добавьте вариативность в последовательность действий — случайные скроллы, клики, переключения вкладок
  • Используйте плавное движение мыши — библиотеки ghost-cursor (Puppeteer) или pyautogui (Selenium)
  • Имитируйте естественную скорость набора текста — посимвольный ввод с паузами на знаках препинания
  • Настройте антидетект-браузеры — Dolphin Anty, AdsPower и Multilogin имеют встроенные механизмы защиты от timing attacks
  • Комбинируйте с качественными прокси — ротация IP снижает возможность статистического анализа

Помните: идеальной защиты не существует. Антифрод-системы постоянно эволюционируют, добавляя новые методы детекции. Ваша задача — максимально приблизить поведение скрипта к реальному пользователю и регулярно обновлять методы обхода.

Если вы работаете с автоматизацией браузеров для парсинга, тестирования или других задач, рекомендуем использовать резидентные прокси — они обеспечивают максимальный уровень анонимности и минимизируют риск детекции по IP-адресу. Для мобильных платформ лучше подходят мобильные прокси, которые имитируют реальных пользователей мобильных операторов.