Коротко: спам в формах и фейковые регистрации делают в основном боты, которые отправляют запросы напрямую на адрес обработчика, минуя страницу. Поэтому лучше работают не одна капча, а несколько простых слоёв: скрытое поле-ловушка, проверка времени заполнения, проверка перехода с вашего сайта, ограничение частоты и проверка запросов на стороне сайта. Ниже примеры кода, которые мы проверили.
Заявки с одинаковым текстом и ссылками, письма «клиентов» с несуществующими адресами, сотни новых аккаунтов за ночь: знакомая картина. Вред не только в мусоре в CRM: менеджеры тратят время, растёт стоимость обработки, а рекламный бюджет уходит на заявки, которые не превращаются в продажи. Разберём, откуда это берётся и как закрыть.
Откуда берутся спам-заявки и фейковые регистрации
В классификации OWASP это два типа автоматизированных угроз: спам (OAT-017, добавление вредоносной или сомнительной информации в формы, комментарии и сообщения) и массовое создание аккаунтов (OAT-019, создание множества учётных записей для последующего злоупотребления). Подробнее обо всех типах: 21 тип автоматизированных угроз на русском.
Технически бот чаще всего не открывает страницу с формой. Он находит адрес обработчика (например, /send или /register) и отправляет на него POST-запросы с готовым набором полей. Отсюда следует главный принцип защиты: проверять надо сам запрос, а не то, что нарисовано на странице.
Как понять, что вы подвергаетесь атаке ботов
- В журнале сервера много POST-запросов к форме с одного адреса или подсети, без предшествовавшего GET страницы.
- Заявки приходят пачками, часто ночью, с похожим текстом или одинаковыми ссылками.
- В поле «Referer» запросов к форме пусто или стоит чужой сайт.
- Регистрации с адресами, которые различаются на пару символов.
Посмотреть это можно в бесплатном анализаторе лога и командами из статьи как найти ботов в access-логе nginx.
Слой 1. Скрытое поле-ловушка и время заполнения
Идея простая. В форму добавляется скрытое поле (оно не видно людям, но бот заполняет все поля подряд) и подписанная метка времени. Если скрытое поле заполнено, или форму отправили мгновенно, или метка поддельная, это бот. Пример на PHP:
const FORM_SECRET = 'замените-на-длинную-случайную-строку';
// При выводе формы: подписанная метка времени
function form_stamp(): string
{
$t = time();
return $t . '.' . hash_hmac('sha256', (string)$t, FORM_SECRET);
}
// При обработке: false = похоже на бота
function form_looks_human(array $post, int $minSeconds = 3, int $maxSeconds = 3600): bool
{
if (!empty($post['website'])) { // honeypot должен остаться пустым
return false;
}
$parts = explode('.', (string)($post['stamp'] ?? ''), 2);
if (count($parts) !== 2 || !ctype_digit($parts[0])) {
return false;
}
if (!hash_equals(hash_hmac('sha256', $parts[0], FORM_SECRET), $parts[1])) {
return false; // подпись не сходится
}
$age = time() - (int)$parts[0];
return $age >= $minSeconds && $age <= $maxSeconds;
}
В HTML формы нужны два дополнительных поля: ловушка, скрытая стилями (<input type="text" name="website" tabindex="-1" autocomplete="off" style="position:absolute;left:-9999px">) и поле с меткой (<input type="hidden" name="stamp" value="<?= form_stamp() ?>">). Мы проверили функцию на примерах: форма, отправленная мгновенно, отклоняется; через 10 секунд с пустой ловушкой проходит; заполненная ловушка, отсутствующая или поддельная метка отклоняются.
Ограничения: простые боты попадаются, продвинутые умеют не трогать скрытые поля и выдерживать паузу. Кроме того, у некоторых плагинов автозаполнения (менеджеры паролей) бывают конфликты с полями-ловушками, поэтому оставляйте поле без осмысленного имени вроде email или phone.
Слой 2. Принимать форму только с вашего сайта (Referer)
Когда посетитель отправляет форму из браузера со страницы вашего сайта, браузер сообщает, откуда он пришёл (заголовок Referer). Бот, который бьёт напрямую в обработчик, обычно его не передаёт. Правило для nginx, которое мы проверили на тестовом сервере:
location = /send {
# POST без перехода с нашего сайта: блокируем
set $bad "";
if ($request_method = POST) { set $bad "P"; }
if ($http_referer !~* "^https?://(www\.)?example\.ru/") { set $bad "${bad}R"; }
if ($bad = "PR") { return 403; }
# ... передача обработчику формы ...
}
Результат проверки: POST без Referer получил 403, POST с Referer вашего сайта прошёл (200), POST с чужим Referer получил 403, а обычный GET страницы не затронут. Замените example.ru и /send на свои значения.
Осторожно: Referer легко подделать, поэтому это защита от простых скриптов, а не от целенаправленных атак. Если на ваших страницах включена политика Referrer-Policy: no-referrer или посетители используют расширения, которые вырезают Referer, правило начнёт отклонять и живых людей. Сначала проверьте на тестовом адресе.
Слой 3. Ограничение частоты отправок
Живой человек не отправляет форму каждую секунду. Ограничьте частоту запросов к обработчику на один IP, например, так:
limit_req_zone $binary_remote_addr zone=forms:10m rate=6r/m;
location = /send {
limit_req zone=forms burst=3 nodelay;
# ... остальное ...
}
Мы проверили конфиг на тестовом сервере: первые четыре запроса подряд (один плюс запас в три) проходят, пятый и следующие получают ответ 503. Шесть запросов в минуту достаточно для людей, но режет серии. Важный нюанс: директива return в том же блоке location срабатывает раньше limit_req и обходит ограничение, поэтому в блоке с ограничением должен стоять реальный обработчик (proxy_pass, fastcgi_pass и т.п.), а не return. Подробнее об ограничениях: rate limiting, а также fail2ban с nginx для автоматического бана.
Слой 4. Подтверждение, а не доверие
- Регистрация. Требуйте подтверждения адреса почты по ссылке перед активацией аккаунта. Фейковые регистрации с несуществующими адресами отсекаются сразу.
- Заявки. Для важных форм можно добавлять подтверждение телефона или почты, но это снижает конверсию: применяйте там, где спама особенно много.
- Данные. Проверяйте формат телефона и почты на сервере, отклоняйте поля со ссылками, если ссылки в них не нужны.
Слой 5. Капча, но не вместо остального
Капча полезна для критичных форм, но защищает только ту форму, где стоит, и только если токен проверяется на сервере. Подробности и цены у Яндекса: Яндекс SmartCaptcha: как подключить, сколько стоит и чем дополнить. Общий разбор видов: CAPTCHA: защита сайта от ботов.
Слой 6. Проверка запросов на стороне сайта
Ручные правила нужно обслуживать, а боты их быстро обходят. Постоянная защита смотрит на запрос целиком: User-Agent, IP и сеть (датацентры и облака), заголовки браузера, частоту запросов и отправку формы без перехода с вашего сайта. Bot Guard среди прочего блокирует POST к формам без перехода с вашего сайта, а подозрительным посетителям показывает тихую проверку браузера вместо капчи. Он подключается к WordPress плагином и к 1С-Битриксу модулем, подробнее о защите форм: honeypot.
В каком порядке внедрять
- Посмотрите журнал: убедитесь, что спам идёт напрямую в обработчик.
- Включите ловушку и метку времени (слой 1): это бесплатно и быстро.
- Добавьте проверку Referer и ограничение частоты (слои 2 и 3), сначала на тестовом адресе.
- Для регистраций включите подтверждение почты.
- Если бот выдерживает всё это, подключайте постоянную защиту и/или капчу.
Частые вопросы
Как защитить форму обратной связи от спама без капчи?
Скрытое поле-ловушка, подписанная метка времени, проверка Referer и ограничение частоты отправок останавливают большую часть простых ботов без заданий для посетителей. Для сильных атак добавляют проверку запросов на стороне сайта.
Почему спам идёт, хотя на форме стоит капча?
Бот может отправлять запросы напрямую на обработчик, а обработчик не проверяет токен капчи на сервере. Либо капчу решают специальные сервисы. Проверяйте токен на сервере и не полагайтесь на одну капчу.
Что такое honeypot в форме?
Это скрытое поле, которое человек не видит и не заполняет, а бот заполняет вместе с остальными. Если поле не пустое, запрос считается ботом.
Можно ли блокировать заявки без Referer?
Можно для простых скриптов, но Referer подделывается, а у части посетителей он вырезается расширениями или политикой сайта. Проверяйте правило на тестовом адресе и следите, не блокируются ли живые люди.
Как отличить фейковую регистрацию от настоящей?
По подтверждению почты (ссылка не открыта), по подозрительным похожим адресам, по регистрации пачками из одной сети, по пустому профилю и мгновенному заполнению формы. Надёжнее всего подтверждение адреса и ограничение частоты.
Защитите свой сайт от ботов прямо сейчас
Подключение занимает несколько минут — вставьте один PHP-сниппет, и Bot Guard начнёт фильтровать ботов, скрейперов и попытки SQL-инъекций автоматически.