Для бизнеса SMS-бомбинг – это не просто раздражающий спам, а реальная операционная, финансовая и репутационная проблема. Каждая отправленная SMS истощает корпоративный счет у SMS-агрегатора. К тому же такой поток сообщений создает дополнительную нагрузку на инфраструктуру. Наконец, страдает клиентский опыт – кому понравится получать сотни SMS в день с одного номера? Эту проблему можно решить с помощью WAF (как это сделать мы уже писали ранее). Но есть более эффективный способ – антибот, для которого это целевая задача.  В этом посте мы разберем, как вредоносные боты организуют подобную атаку и какие механизмы детекта используют решения класса антибот.

Откуда берутся лишние SMS?

SMS-бомбинг – это кибератака на endpoint'ы, отвечающие за отправку SMS. Ее цель:

  • израсходовать бюджет на отправку сообщений;

  • создать DDoS на телефонные номера третьих лиц;

  • перегрузить инфраструктуру сервиса;

  • обойти rate limiting для брутфорса OTP-кодов (одноразовых кодов подтверждения).

Причём атакующему не нужно ничего взламывать. Ваш сервис сам всё делает – просто “слишком охотно”.

Представьте стандартную страницу регистрации, где есть поля: имя, e-mail, номер телефона. А также кнопка «Получить код».

Где-то в этот момент в интернете уже нашёлся человек, которому не нравится ваш продукт, конкурент с нехорошими мыслями, или просто скучающий подросток. Он смотрит на вашу форму и думает: «А что будет, если нажать эту кнопку 10 000 раз?».

И нажимает. Конечно, не сам, а с помощью бота.

С точки зрения сервера – это легитимные POST-запросы.

С точки зрения SMS-шлюза – это реальные SMS, которые кто-то получает.

С точки зрения вашего кошелька – это деньги, которые улетают в никуда.

С точки зрения абонента, которому прилетело 3000 SMS за ночь, – это повод позвонить в поддержку и написать гневный отзыв о компании.

Приведем простой расчет: если цена одного SMS-сообщения для бизнеса составляет 0,5–2 рубля (в зависимости от агрегатора и страны), то атака в 10 000 запросов обойдется в 20 000 рублей. Вроде немного, но при DDoS-рассылке на 1 млн номеров (что не редкость для ботнетов) потери могут достигать 2 млн рублей за одну ночь — а это уже значительно. И это только прямые расходы, не считая затрат на поддержку и отток клиентов.

Зоопарк ботов

Врага надо знать в лицо, поэтому давайте разберем, какие именно боты могут рассылать пакеты SMS.

Кейс 1. Прямой HTTP-запрос в цикле

Это самый распространенный сценарий автоматизации запросов. Злоумышленник открывает DevTools, находит POST-запрос на /api/send-sms, копирует как curl и запускает в цикле (многократная повторная отправка).

#!/bin/bash
TARGET="https://example.com/api/send-sms"
PHONE="+79991234567"
for i in $(seq 1 1000); do
  curl -s -X POST "$TARGET" \
    -H "Content-Type: application/json" \
    -d "{\"phone\": \"$PHONE\"}" &
done
wait

Злоумышленник никак не маскируется. Он использует один IP, тысячи запросов в секунду, стандартный curl/7.88.0 в User-Agent, нет cookies, JavaScript не выполняется. Браузер вообще не открывался – все действия выполняются через терминал.

Признаки атаки: один IP-адрес с аномальной частотой запросов, отсутствие cookies, дефолтный TLS-стек libcurl, который не имеет ничего общего с заявленным (или отсутствующим) браузером.

Как защищает антибот

Разбор TLS-handshake. Устанавливая HTTPS-соединение, клиент оставляет характерный «отпечаток» – набор и порядок cipher suites, TLS extensions, версию протокола, алгоритмы сжатия. Chrome, Firefox, curl и Python оставляют принципиально разные отпечатки, подделать которые на прикладном уровне нельзя, и нужно менять сам TLS-стек. Если клиент молчит о себе или заявляет браузер, а fingerprint выдаёт libcurl – запрос отсекается ещё на уровне соединения, до бизнес-логики.

JavaScript и Cookie challenge. Перед тем как запрос дойдёт до SMS-шлюза, клиент (браузер) должен принять cookie и решить небольшую задачу в браузере, вернув результат. Если действует злоумышленник через терминал, то JavaScript не будет запущен и ответа не будет. Проверка на выполнение JavaScript – это дёшевый и быстрый способ отсечь все запросы без браузера.

Организовать такую проверку можно с помощью решений класса антибот.

Кейс 2. Python с ротацией прокси и User-Agent

Более сложный вариант – попытка скрыть автоматизацию через ротацию IP с помощью прокси и рандомизацию User-Agent. Такие скрипты лежат на GitHub и расходятся по тематическим каналам.

import requests
import random
from concurrent.futures import ThreadPoolExecutor

TARGET = "https://example.com/api/send-sms"

PROXIES = ["http://1.2.3.4:8080", "http://5.6.7.8:3128", ...]
USER_AGENTS = [
    "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36...",
    "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)...",
    "Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X)...",
    ...
]

def send_request(phone):
    try:
        requests.post(
            TARGET,
            json={"phone": phone},
            headers={"User-Agent": random.choice(USER_AGENTS)},
            proxies={"https": random.choice(PROXIES)},
            timeout=5
        )
    except:
        pass

with ThreadPoolExecutor(max_workers=50) as executor:
    executor.map(send_request, ["+79991234567"] * 5000)

Признаки атаки: User-Agent заявляет «Chrome на Windows», но  в TLS видно python-requests + urllib3, потому что Chrome и Python шифруют по-разному. Бесплатные публичные прокси (которыми скрывают IP) часто сами в чёрных списках. Cookies между запросами не сохраняются, JavaScript не выполняется совсем (проверить это можно так же, как и в 1 кейсе).

Как защищает антибот

Валидация User-Agent против реальных возможностей клиента. Проверка UA – это не доверие к строке, а сверка заявленного с фактическим. Если клиент представляется Chrome, но не шлёт заголовки Sec-CH-UA, не поддерживает HTTP/3, не выполняет JavaScript, а TLS fingerprint не совпадает с Chrome, значит, это не Chrome.

Device fingerprint. Сессии не связаны между собой: 5000 запросов, и каждый выглядит как новый «пользователь» с одинаково пустым профилем. Такой паттерн сам по себе является аномалией.

Кейс 3. Headless-браузер

Здесь уже настоящий Chromium: выполняет JavaScript, принимает cookies, рендерит страницу. С точки зрения сервера почти неотличим от живого пользователя.

import asyncio
from playwright.async_api import async_playwright

async def run(phone, count):
    async with async_playwright() as p:
        browser = await p.chromium.launch(headless=True)
        for i in range(count):
            ctx = await browser.new_context(
                user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64)...",
                viewport={"width": 1920, "height": 1080},
                locale="ru-RU",
            )
            page = await ctx.new_page()
            await page.goto("https://example.com/register")
            await page.fill('input[name="phone"]', phone)
            await asyncio.sleep(0.5)  # имитируем "думающего" пользователя
            await page.click('button[type="submit"]')
            await ctx.close()

asyncio.run(run("+79991234567", 500))

Признаки атаки: headless-браузеры имеют характерные fingerprint-признаки. navigator.webdriver === true. WebGL-рендерер – SwiftShader или llvmpipe вместо реальной видеокарты. Canvas рендерится программно и отличается от аппаратного. Нет сохранённых шрифтов и плагинов. Поведение – идеально равномерные интервалы без микровариаций, мышь не двигается совсем.

Как защищает антибот

Параметры браузера и окружения. Углублённый анализ браузерного окружения, заточенный под детекцию headless и эмуляторов:

  • navigator.webdriver, отсутствие window.chrome, пустой navigator.plugins – базовые, но до сих пор работающие проверки;

  • WebGL: реальный Chrome покажет что-то вроде ANGLE (NVIDIA GeForce RTX 3060...), headless – Google SwiftShader;

  • Canvas fingerprint: программный рендеринг даёт характерный, легко узнаваемый результат;

  • Audio Context: реальное «железо» при обработке аудиосигнала даёт слегка уникальный результат, эмулятор – одинаковый на всех машинах;

  • timezone, не совпадающий с геолокацией IP.

Ни один признак сам по себе не приговор, но в совокупности они дают уверенную оценку.

Поведенческий анализ первый сигнал. Мышь не двигалась. Форма «заполнилась» за 50 миллисекунд. Паузы между действиями идеально равномерные. Активность реального пользователя так не выглядит.

Кейс 4. Настоящий браузер, но бот внутри

Мы разобрали headless-браузеры. Но злоумышленник пошёл дальше — теперь он использует настоящий браузер с установленным расширением или скриптом автонажатия. Технически это легитимный Chrome: реальный fingerprint, реальный Canvas, реальный WebGL. Сервер видит обычного пользователя. Но поведение выдаёт. 

// Пример скрипта автонажатия в настоящем браузере
setInterval(() => {
  document.querySelector('input[name="phone"]').value = '+79991234567';
  document.querySelector('button[type="submit"]').click();
}, 1500);

Сравните две сессии.

Бот. Заходит сразу на /register. Скрипт мгновенно подставляет номер и кликает каждые 1500 мс как по расписанию. В его сессии — одна страница, один URL, цикл из одинаковых действий. Мышь не двигалась, скролла нет, переходов между страницами нет.

Человек. Начал с главной страницы, посмотрел каталог, открыл карточку товара, прочитал отзывы. Потом перешёл в раздел «Контакты», оттуда — на страницу регистрации. Ввёл номер с паузами, ошибся — исправил. После получения SMS перешёл в личный кабинет. Вся сессия — это цепочка переходов: / → /catalog → /product/123 → /contacts → /register → /profile. Мышь двигалась, скролл был, наведения на элементы — всё как у живого человека.

Как защищает антибот

Бот оставляет после себя одну страницу и тысячи однотипных запросов. Человек — длинную сессию с переходами, реферерами, случайными движениями, разными таймингами. Поведенческий анализ видит это: у бота нет истории, нет пути, нет «шума». Только прямой удар в цель.

И если тысяча сессий выглядит одинаково — все пришли на /register, ни одна не зашла на другие страницы, все имеют идеальные тайминги — это кластер ботов. Настоящий браузер не спасает, когда внутри него сидит скрипт.

Именно поэтому атакующие идут дальше. Они начинают имитировать путь человека.

Кейс 5. Адаптивные боты с обходом детекции

Самый сложный случай – это боты, обученные имитировать поведение реальных пользователей и адаптирующиеся к защите в реальном времени.

// Псевдокод адаптивного бота

Модуль поведения (обучен на записях реальных сессий):
  generateMousePath(from, to):
    // Траектория с ускорением, замедлением, дрожанием и микропаузами
    вернуть BezierCurve(from, to) + GaussianNoise(σ=0.3)

  generateTyping(text):
    // WPM варьируется, есть опечатки и backspace, паузы перед сложными буквами
    вернуть KeyEvents(text, wpm=45..85, error_rate=0.03)

Модуль fingerprint:
  mimicDevice(profile):
    // Canvas: шум, характерный для конкретного GPU
    // WebGL: параметры реального устройства из базы
    // Audio: синтез fingerprint под конкретный CPU
    // Fonts: набор, характерный для OS и региона 

Адаптация:
  при обнаружении challenge-page → замедлиться, сменить профиль
  при обнаружении CAPTCHA     → отправить в сервис решения
  при блокировке IP           → запросить новый residential proxy
  при поведенческой аномалии  → увеличить случайность паттернов

Признаки атаки: даже хорошая имитация оставляет статистические следы. Траектории мыши «слишком хороши». Минимальные, но стабильные отклонения canvas fingerprint от заявленного устройства. Временны́е интервалы, неправдоподобные для человека.

Как защищает антибот

Нужен статистический анализ на накопленных данных. Единичный запрос ничего не скажет, а паттерн из сотен сессий – скажет многое.

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

Многоуровневая защита: как складываются слои

Дашборд продукта SmartBot Protection
Дашборд продукта SmartBot Protection

Если собрать всё вместе, эффективная защита – это не одна проверка, а слоёный торт. Каждый слой отсекает свой класс атак, а запрос проходит их последовательно, накапливая «уровень подозрения».

Слой 1. TLS-проверка. Анализ TLS-отпечатка на уровне соединения. Отсекает curl, Python requests и большинство самодельных HTTP-клиентов ещё до бизнес-логики.

Слой 2. JavaScript и Cookie проверки. Первичный фильтр: умеет ли клиент выполнять JS и хранить cookie. Отсекает весь транспортный уровень без браузера.

Слой 3. Device fingerprinting. Сбор десятков характеристик устройства в единый отпечаток, не зависящий от IP и cookie. Позволяет узнавать устройство даже за NAT и видеть ботнет как один источник.

Слой 4. Signals. Углублённая браузерная проверка: canvas, WebGL, audio context, шрифты, плагины, разрешение экрана, флаги автоматизации. Заточена под headless-браузеры и эмуляторы.

Слой 5. Поведенческий анализ. Анализ того, как клиент себя ведёт: скорость запросов, паттерны навигации, временны́е характеристики, движения мыши, скорость заполнения форм. Последний и самый интеллектуальный рубеж.

Поверх слоёв – система списков (white/black/grey для IP, User-Agent и fingerprint, с ручным и автоматическим управлением) и DNS-валидация для легитимных ботов. к последним относятся Googlebot и Яндексбот, которые проверяются через reverse DNS lookup и пропускаются без ограничений.

Итог

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

Эффективная защита строится слоями: каждый отсекает свой класс атак и заставляет злоумышленников усложнять свои атаки и тратить на них больше ресурсов. TLS-проверка убирает примитивные инструменты, browser signals – headless-браузеры, поведенческий анализ и кластеризация – распределённые атаки.

Автор: Кирилл Ященко, системный аналитик WMX

Комментарии (0)