В январе 2024-го к нам пришёл клиент-агентство: «У нас лидген-форма крупного банка стала принимать по 1500-2500 заявок в день, все левые. 90% — с реальными email-адресами, но имена бессмысленные, телефоны существуют, но не отвечают, города выбраны случайно из выпадающего списка. Бизнес теряет 4 часа в день, проверяя весь этот мусор».
Картина классическая для 2026-го: после релиза LLM-агентов с computer use заваливать формы стало проще, чем раньше. Боту не нужно решать капчу — он просто проходит её через GPT-4o. Хорошая новость: спам-боты против форм всё равно ловятся в пять слоёв, ни один из которых не требует капчи. И настраивается это за час.
Пять слоёв против форм-спама в порядке эффективности: honeypot (ловит ~50% самых ленивых), time-to-submit (отсекает агентов, которые шлют POST за 200мс), behavioral score (mousemove, focus, scroll), email-валидация (disposable-домены), rate-limit по IP+fingerprint. CAPTCHA можно вообще не подключать — в 2026-м она пробивается GPT-агентами в 87% случаев.
Почему капча — это уже не серебряная пуля
reCAPTCHA v2 («выберите все автобусы»), reCAPTCHA v3 (невидимый score) и hCaptcha сегодня все решаются LLM-агентами. GPT-4o с computer use смотрит на скриншот, кликает по нужным квадратам, ждёт пару секунд — и проходит. Точность ~87%. Сервисы вроде 2Captcha и CapSolver работают через те же LLM в фоне за $0.001 за решение.
При этом капча всё ещё снижает конверсию реальных пользователей на 5-15%. То есть в худшем сценарии вы получаете меньше заявок от людей и почти столько же от ботов. Прежде чем подключать капчу, имеет смысл сначала проверить, не хватит ли пяти бескапчевых слоёв.
Слой 1. Honeypot — скрытое поле, которое заполнит только бот
Самый дешёвый и самый эффективный фильтр. В форме добавляется поле с осмысленным именем (name="website" или name="address_2"), которое спрятано визуально:
<input type="text" name="website" tabindex="-1"
autocomplete="off"
style="position:absolute; left:-9999px; opacity:0; pointer-events:none">
Реальный пользователь это поле не видит и не трогает. Большинство ботов — особенно простые, на Puppeteer или старых curl-скриптах — заполняют все поля формы подряд. Если в website после сабмита что-то есть — это бот, отказываем без объяснений.
По нашим данным, honeypot в одиночку ловит 40-55% спам-трафика на лидген-формах. Стоимость внедрения — пять минут.
Топовая ошибка с honeypot — забыть про autocomplete="off". Браузер Chrome агрессивно автозаполняет поля с именами вроде email, name, address. Если honeypot называется email_secondary — Chrome туда подставит email, и реальный пользователь зафейлит проверку. Используйте имена, которые Chrome не заполняет: website, company_url, fax.
Слой 2. Time-to-submit
Человеку нужно время, чтобы заполнить форму. Даже короткая (email + телефон) занимает 8-15 секунд. Бот шлёт POST через 200-800мс после загрузки страницы. Разница огромная.
Реализация: при загрузке страницы сохраняем timestamp в скрытом поле или localStorage, при сабмите проверяем разницу:
// На странице
document.getElementById('ts').value = Date.now();
// На сервере
const elapsed = Date.now() - submittedTimestamp;
if (elapsed < 3000) {
return reject('too fast');
}
if (elapsed > 30 * 60 * 1000) {
return reject('session expired');
}
Пороги в 3 секунды снизу и 30 минут сверху — разумная вилка. Совсем медленный сабмит часто означает, что страница висит во вкладке часами и timestamp устарел — это уже не валидная сессия. Слой 2 ловит ~20% спама поверх первого.
Слой 3. Поведенческие сигналы
Человек двигает мышью, фокусирует поля, иногда стирает символы и переписывает. Бот часто — нет. Собираем сигналы в локальный JS:
- Было ли движение мыши вообще на странице (
mousemove); - Было ли касание на мобильном (
touchstart); - Фокусились ли в полях руками (а не через
field.focus()); - Печатали ли в инпутах вживую (
keydownс реалистичными интервалами 80-300мс между нажатиями); - Был ли скролл страницы.
Все сигналы суммируются в один behavior_score от 0 до 100. Score ниже 25 — почти наверняка автоматизация. Между 25 и 60 — серая зона (пусть проходит, но логируется). Выше 60 — реальный человек.
Сами сигналы можно собирать в TDS-виджет в одну строчку:
<script src="https://api.tds.so/widget.js"
data-form-id="banking-leadgen"></script>
Виджет добавит скрытое поле tds_behavior с зашифрованным score, который проверяется на бэкенде через наш API. Без js-виджета можно реализовать всё то же самое самостоятельно — нужно 50 строк кода и одно поле в форме.
Слой 4. Email-валидация без отправки писем
Большинство спамеров используют либо disposable-email-сервисы (10minutemail, guerrillamail, tempmail), либо неаккуратно перечисляют что-то вроде test@gmail.com, asdf@yahoo.com. Простая проверка:
- Поддерживаем список ~3000 disposable-доменов (есть готовые на GitHub, обновляются автоматически).
- Проверяем MX-запись домена синхронно (5мс) — если её нет, ящик не существует.
- Проверяем «правдоподобность» local-part — много цифр подряд, нет гласных, длина больше 30 символов — флаги.
Не нужно делать SMTP-handshake против Gmail/Outlook — они закроют ваш IP за «email enumeration» через час. MX + disposable-list дают 90% эффекта без юридических и технических рисков.
Слой 5. Rate-limit по IP и fingerprint
Финальный слой. Любая форма должна иметь ограничения:
- Не более 1 сабмита в минуту с одного IP (Redis-counter с TTL=60s).
- Не более 5 сабмитов в час с одного fingerprint (на случай если бот меняет IP через резидентный прокси).
- Не более 50 сабмитов в день с одного /24-подсети (на случай ботнета с IP в одной подсети).
Без rate-limit ваша форма уязвима к простой атаке: бот меняет данные и шлёт по сабмиту в секунду в 8 параллельных потоков. За час — 28 000 левых лидов. С rate-limit — максимум 60 в час с одного IP, и ботнет атакующего быстро упирается в потолок.
Сигнатуры спама в логах
Когда ваши слои работают, в Sentry/логах появляются характерные паттерны. По ним легко аудитить, не пропускаете ли что-то:
# Боты на честных Chrome user-agent, но без mousemove
form_rejected: reason=behavior_score_low score=8
form_rejected: reason=behavior_score_low score=12
form_rejected: reason=behavior_score_low score=4
# Боты на disposable-email
form_rejected: reason=disposable_email domain=tempmail.com
# Скрипты, не сабмиттящие через интерфейс — шлют чистый POST
form_rejected: reason=honeypot_filled value="https://<long-url>"
# Атака на скорость — много сабмитов за минуту с одного IP
form_rejected: reason=rate_limit ip=185.X.Y.Z count=14/min
Если в логах одна и та же причина в 95% случаев — значит, остальные слои не нужны. Если распределение разное — все пять работают и достают свою долю.
Когда всё же подключить капчу
Капча оправдана в трёх случаях:
- Финансовые формы — банковские лиды, ввод платёжных данных. Здесь цена false negative ($100-1000 за пропущенного скамера) кратно выше цены отказа реального человека.
- Целевая атака — конкретный конкурент или хейтер целенаправленно фарзит вашу форму, и пяти слоёв не хватает. Бывает редко.
- Compliance — некоторые регуляторы (PCI, отдельные финансовые лицензии) требуют наличие интерактивной проверки.
В остальных случаях пяти слоёв без капчи достаточно. И ваши клиенты-люди не будут страдать от автобусов на картинках.
Вывод
Вернёмся к клиенту из начала. Через 1 час мы накатили на их форму: honeypot + time-to-submit + TDS-виджет с behavior_score + email-валидация + rate-limit. Через 12 часов поток левых заявок упал с 2000 до 80 в сутки. Через неделю — до 20. Капчу не включали.
Если форма ловит больше 50 спам-заявок в день — стоит проходить эти пять слоёв по порядку. Если меньше — обычно достаточно одного honeypot + email-валидации. Главное правило: проверяйте каждый слой отдельно через логи, чтобы понимать, какой что ловит.