Установка и настройка

Всё, что нужно, чтобы поставить BotOff на сайт: от установки в один клик в Битриксе до ручной проверки в своём коде.

Быстрый старт

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

Выберите свой вариант:

Требования

  • PHP 7.4 или новее. Расширение sodium желательно (оно есть почти везде); без него задача станет проще для ботов, но всё будет работать.
  • Возможность записывать файлы в каталог над корнем сайта (туда кладётся конфиг с ключом и журнал).
  • Для модуля Битрикса — MySQL или MariaDB и сайт в UTF-8.
  • Ни Composer, ни SSH, ни cron не нужны. Подходит обычный виртуальный хостинг.

Скачать

Модуль для 1С-Битрикс версия 0.3.5 · 149 КБ
botoff.captcha — распаковать в /local/modules/ SHA-256: 34edfcee4274e808b99be98aaac596839dd0c6f0737778bcc6a1959f1f0952b3
Скачать .zip
PHP-пакет версия 0.2.4 · 89 КБ
папка botoff/ — для любого сайта на PHP и WordPress SHA-256: 5ef1b64360180998589c59ba8fae132c625089cee944da674efe842e62d788f5
Скачать .zip

Проверить, что архив скачался целиком:

консоль
# Linux, macOS
sha256sum botoff-php-*.zip

# Windows (PowerShell)
Get-FileHash botoff-php-*.zip -Algorithm SHA256

1С-Битрикс

  1. Распакуйте архив в /local/modules/ Должна получиться папка /local/modules/botoff.captcha/ (в ней include.php, install/, lib/). Залить можно по FTP или через файловый менеджер хостинга.
  2. Установите модуль Админка → Marketplace → Установленные решения → в строке «BotOff — защита форм от ботов» нажмите Установить.
  3. Всё Модуль сразу начинает проверять формы в режиме наблюдения: ничего не блокирует, только записывает. Виджет на страницы вставляется сам.

Что модуль создаёт при установке

ЧтоГдеЗачем
Папка виджета/botoff/Скрипт виджета и точка входа, к которой он обращается
Конфиг/bitrix/php_interface/botoff-config.phpКопия настроек для /botoff/; пересобирается при сохранении настроек
Служебные данные/bitrix/botoff-data/Использованные токены, счётчики частоты (закрыто от веба)
Таблицаb_botoff_decisionЖурнал проверок для отчёта, хранится 30 дней
Обработчикиmain:OnPageStart, main:OnEndBufferContentПроверка формы до компонентов и вставка виджета перед </body>
Агент\BotOff\Bitrix\Agent::cleanup();Раз в сутки удаляет записи журнала старше 30 дней

Где что найти в админке

  • Настройки → BotOff → Отчёт — сколько отправок прошло и сколько было бы отклонено, по каким причинам и на каких адресах.
  • Настройки → Настройки модулей → BotOff — режим, адреса форм, исключения.

Настройки модуля

ПолеЧто делает
РежимНаблюдение — проверять и записывать; Защита — блокировать; Выключено — модуль ничего не делает.
Какая форма на каком адресеПо строке на адрес: /путь/ => действие. От действия зависит строгость (см. профили). Всё, что не указано, считается обычной формой form.
Не проверять адресаПо строке на адрес, совпадение по началу пути. Для AJAX-обработчиков, которые отправляются не из <form>.
Отклонять формы без токенаЕсли включено, форма от посетителя с выключенным JavaScript в режиме защиты не пройдёт. Боты почти всегда без JavaScript.
Подключать виджет автоматическиЕсли выключить — вставьте тег сами (ниже).
Проверок в минуту с одной подсетиОграничение частоты задач. По умолчанию 60; меньше не ставьте — за одним адресом мобильного оператора сидят тысячи людей.
IP посетителя брать изЕсли сайт за прокси или Cloudflare, выберите нужный заголовок, иначе все посетители будут «с одного IP».

Пример поля «Какая форма на каком адресе» (такие строки стоят по умолчанию):

настройки модуля
/auth/ => login
/login/ => login
/personal/register/ => signup
/personal/order/make/ => order

Пример поля «Не проверять адреса»:

настройки модуля
/ajax/calc.php
/local/ajax/
/api/
Уже исключены и не требуют настройки: уведомления платёжных систем (ЮKassa, Т-Банк, CloudPayments, Robokassa, PayKeeper, /bitrix/tools/sale_ps_result.php), обмен с 1С, админка, /bitrix/services/, /bitrix/tools/, AJAX корзины и оформления заказа (sale.order.ajax, sale.basket.basket, catalog.*).

Если виджет вставляете сами

Когда автоматическая вставка выключена, добавьте в шаблон сайта перед </body>:

footer.php шаблона
<?php
if (\Bitrix\Main\Loader::includeModule('botoff.captcha')) {
    // false — без привязки в HTML: безопасно для композитного кеша
    echo \BotOff\Bitrix\Settings::boot()->scriptTag('/botoff', null, false);
}
?>

Композитный сайт и HTML-кеш

Поддерживается. Модуль вставляет виджет без персональных данных, поэтому кешированная страница одинакова для всех, а привязку токена к сессии сервер берёт из cookie в момент проверки.

Обновление

Залейте новую версию поверх папки /local/modules/botoff.captcha/. Настройки и журнал сохраняются. Файлы виджета в /botoff/ обновятся при переустановке модуля, либо скопируйте их из install/public/botoff/ вручную.

Ограничения версии 0.1. Только MySQL/MariaDB и сайты в UTF-8. AJAX-формы, которые отправляются не через <form> (например, через BX.ajax по клику), в режиме защиты придётся либо исключить, либо отправлять с токеном (как) — отчёт покажет такие адреса.

Любой сайт на PHP

Быстрее всего — архив из кабинета. Добавьте сайт в кабинет и нажмите «Скачать BotOff для вашего сайта»: внутри уже вписаны ключи и лежит файл подтверждения. Распакуйте архив в корень сайта — сайт подтвердится сам. Затем откройте https://ваш-сайт/botoff/?r=setup: там одна строка для автозащиты, уже с правильным путём под ваш сервер, и кнопка «Копировать». Режим, адреса и исключения дальше меняются в кабинете. Ниже — ручная установка из общего архива.
  1. Залейте папку botoff/ в корень сайта Из архива PHP-пакета. Должно получиться https://ваш-сайт/botoff/index.php.
  2. Включите автозащиту форм Нужно, чтобы файл botoff/guard.php выполнялся перед каждым запросом. Выберите способ, который подходит вашему хостингу (ниже).
  3. Вставьте тег виджета в общий шаблон страниц, перед </body>.
  4. Проверьте, что всё на месте, и неделю понаблюдайте.

Шаг 2. Автозащита: три способа

Способ А — .htaccess (Apache с PHP как модулем). Добавьте строку в .htaccess в корне сайта, путь — полный, от корня сервера:

.htaccess
php_value auto_prepend_file "/home/user/site.ru/public_html/botoff/guard.php"
Если после этого сайт отвечает ошибкой 500 — у вас PHP через FPM/CGI, и php_value там запрещён. Уберите строку и используйте способ Б.

Способ Б — .user.ini (PHP-FPM, nginx, большинство виртуальных хостингов). Создайте файл .user.ini в корне сайта:

.user.ini
auto_prepend_file = /home/user/site.ru/public_html/botoff/guard.php
PHP перечитывает .user.ini раз в 5 минут — изменения вступят в силу не сразу.

Способ В — строка в коде. Если у сайта одна точка входа (index.php), подключите защиту первой строкой:

index.php
<?php
require __DIR__ . '/botoff/guard.php';
// ... дальше ваш код как был
Если в auto_prepend_file уже указан другой файл, второй указать нельзя — добавьте require '/путь/к/botoff/guard.php'; в начало того файла.

Шаг 3. Тег виджета

Простой вариант — статический тег:

HTML
<script src="/botoff/botoff.js" data-endpoint="/botoff" defer></script>

Лучше — через PHP: тогда токен привяжется к сессии посетителя, а виджет узнает, какая форма на каком адресе (routes из конфига):

шаблон сайта
<?php
require_once $_SERVER['DOCUMENT_ROOT'] . '/botoff/bootstrap.php';
echo \BotOff\BotOff::boot()->scriptTag('/botoff');
?>

Шаг 4. Проверка

Откройте в браузере https://ваш-сайт/botoff/index.php?r=health. Должно быть:

ответ
{"ok":true,"v":"0.1.7","alg":"argon2id-v1","network":"up"}

alg = argon2id-v1 — sodium есть, всё отлично. sha256-v1 — sodium выключен: попросите хостинг включить расширение, защита станет сильнее.

При первом запросе BotOff сам создаст на уровень выше корня сайта:

структура
/home/user/site.ru/
├── botoff-config.php      ← ключ и настройки (никому не передавайте)
├── botoff-data/           ← журнал и служебные файлы
└── public_html/
    └── botoff/            ← то, что вы залили
Настройки — в кабинете, без правки файлов. Добавьте сайт в кабинет, подтвердите его и впишите в botoff-config.php две строки — 'site_key' => 'bo_s_…' и 'site_secret' => 'bo_k_…'. Дальше режим, JSON-адреса и исключения меняются на странице «Настройки защиты» сайта в кабинете: пакет сам заберёт их (подписанными ключом сайта) в течение 10 минут, а отчёт и проверка сайта появятся там же. Если botoff.app недоступен, сайт работает на последних полученных настройках.

Если у вас nginx без Apache

Файл .htaccess в папке botoff/ закрывает служебные файлы только на Apache. Для nginx добавьте в конфиг сайта:

nginx
location ~ ^/botoff/(src/|bootstrap\.php|guard\.php|report\.php|config\.example\.php) {
    return 404;
}

Для Caddy:

Caddyfile
@botoff_internal path /botoff/src/* /botoff/bootstrap.php /botoff/guard.php /botoff/report.php /botoff/config.example.php
respond @botoff_internal 404

WordPress

Отдельный плагин ещё в работе, а пока ставится PHP-пакет — так же надёжно.

  1. Залейте botoff/ в корень WordPress (рядом с wp-config.php).
  2. Автозащита — способ Б (.user.ini) из раздела выше, или первой строкой в wp-config.php:
    wp-config.php, в самом начале
    <?php
    require __DIR__ . '/botoff/guard.php';
  3. Виджет — в functions.php вашей темы (лучше дочерней):
    functions.php
    $botoff_widget = function () {
        require_once ABSPATH . 'botoff/bootstrap.php';
        echo \BotOff\BotOff::boot()->scriptTag('/botoff');
    };
    add_action('wp_footer', $botoff_widget);     // все страницы сайта
    add_action('login_footer', $botoff_widget);  // wp-login.php: у неё свой шаблон без wp_footer

Комментарии, вход (wp-login.php), регистрация и обычные формы защищаются сразу. Чтобы вход проверялся строже, добавьте в конфиг:

botoff-config.php
'routes' => [
    '#^/wp-login\.php#' => 'login',
    '#^/wp-comments-post\.php#' => 'comment',
],
Contact Form 7 и другие плагины, которые отправляют формы через REST API (/wp-json/), по умолчанию не проверяются: /wp-json/ в исключениях, иначе сломались бы REST-запросы самого WordPress. Для таких форм проверяйте токен в обработчике плагина (как).
Запросы к admin-ajax.php с сайта (например, «В корзину» в WooCommerce) автозащита проверяет. Если в отчёте на этом адресе все отправки без токена — передавайте токен в запросе или добавьте адрес в исключения.

Кабинет: отчёт, проверка сайта, настройки, оповещения

Кабинет на botoff.app/account работает для любого сайта — с Битриксом и без. Вход по коду из письма, пароля нет.

  1. Добавьте сайт и подтвердите его. Файлом в корне, файлом в папке botoff/ (он уже лежит в архиве из кабинета) или записью в DNS.
  2. Впишите ключи сайта. Битрикс: Настройки → Настройки модулей → BotOff → «Сеть BotOff». PHP-пакет: ключи уже в архиве из кабинета, либо две строки site_key и site_secret в конфиге.
  3. Готово. Сайт раз в 10 минут присылает сводку: адреса форм, итоги проверок, тип клиента и имена полей — без IP-адресов и того, что вводят посетители.

Что есть в кабинете

  • Отчёт — тот же, что в админке Битрикса: график, кто ломится, почему отклонено, живые подсказки «этот адрес стоит защищать», «а это похоже на вебхук — не проверять». Кнопки применяют настройку на сайте сами.
  • Проверка сайта — смотрит сайт снаружи, как посетитель: движок, формы, куда скрипты отправляют данные. Только чтение, формы не отправляются.
  • Настройки защиты — для сайтов без админки: режим, JSON-адреса, исключения, адрес → действие. Сайт забирает их со сводкой, проверяя подпись ключом сайта. Если botoff.app недоступен — работает на последних полученных.
  • Оповещения — письмо об атаке (порог выбираете сами), если сайт перестал выходить на связь, недельная сводка. Отписка в один клик из любого письма.
  • Архив для сайта — PHP-пакет с вписанными ключами и файлом подтверждения. Последний шаг — открыть https://ваш-сайт/botoff/?r=setup: там строка для автозащиты с точным путём под ваш сервер и кнопка «Копировать».

Какие формы защищаются

Автозащита проверяет запросы POST с телом формы (application/x-www-form-urlencoded или multipart/form-data). Не проверяются:

  • GET-запросы (поиск, фильтры);
  • JSON-запросы — у API обычно своя авторизация;
  • адреса из списка исключений.

Виджет сам находит на странице все формы с method="post", в том числе появившиеся позже (модальные окна), и добавляет в них два скрытых поля: botoff-token и поле-ловушку botoff-hp. До вашего кода эти поля не доходят — защита их убирает.

Действия и строгость

У каждой формы есть «действие». Токен выдаётся под конкретное действие: токен от формы обратной связи не подойдёт для входа. Действие задаёт строгость:

ПрофильДействияМакс. рискЕсли наша сеть недоступна
formsform, feedback, comment70пускаем
shoporder, review60пускаем
authlogin, signup, reset50пускаем, только если сеть действительно лежит
strictpayment40не пускаем

Пока сайт не подключён к сети узлов BotOff (как сейчас у всех — сеть в разработке), все проверки локальные и все профили работают.

Как указать действие

По адресу — в конфиге (routes) или в настройках модуля Битрикса. Виджет получит ту же таблицу и сам выдаст токен с нужным действием:

botoff-config.php
'routes' => [
    '#^/auth/#' => 'login',
    '#^/register/#' => 'signup',
    '#^/cart/checkout#' => 'order',
],

Атрибутом формы — если адресов несколько или удобнее в HTML. Атрибут главнее таблицы, но сервер ждёт действие по таблице — они должны совпадать:

HTML
<form method="post" action="/auth/" data-botoff-action="login">

Отключить защиту для конкретной формы (виджет её не тронет; на сервере добавьте адрес в исключения):

HTML
<form method="post" action="/subscribe" data-botoff="off">

Режим наблюдения и включение защиты

После установки BotOff ничего не блокирует: проверяет каждую форму и записывает, что было бы отклонено. Так вы увидите, не задевает ли защита что-то своё — например, AJAX-запрос, который отправляется без формы.

Где смотреть

  • Битрикс — Настройки → BotOff → Отчёт. У каждого адреса — последний отказ: какие поля пришли, каким клиентом и откуда. Кнопка «В исключения» рядом.
  • PHP-пакет — консольный отчёт по журналу (нужен SSH):
    консоль
    php botoff/report.php 7
    Без SSH скачайте по FTP файл botoff-data/decisions.log — по строке JSON на каждую проверку.

Как читать

  • Отказы honeypot, replayed, отправки без браузера (curl, python) — это боты, всё правильно.
  • На адресе все отправки отклонены с missing_token, и это ваша форма — значит, она отправляется не через <form>: исключите адрес или передавайте токен.
  • wrong_action — действие формы не совпало с таблицей routes. Поправьте таблицу или атрибут.

Включить защиту

Битрикс — кнопка «Включить защиту» в отчёте. PHP-пакет — в конфиге:

botoff-config.php
'mode' => 'enforce',

Посетитель, не прошедший проверку, увидит страницу «Не получилось отправить форму» с кнопкой вернуться. AJAX-запрос с заголовком Accept: application/json получит ответ 403:

ответ
{"error":"botoff_rejected","message":"Не удалось подтвердить, что запрос отправил человек. Обновите страницу и попробуйте ещё раз."}

Как BotOff ловит ботов

Одной проверки мало: бот подделает то, что проверяют. Поэтому BotOff сверяет несколько слоёв и поднимает цену для тех, кто не сходится. Человек ничего этого не замечает.

СлойЧто проверяетсяЧто получает бот
Задача в браузереArgon2id: память и процессор. Сложность растёт с риском — человеку доли секунды, подозрительному клиенту в десятки раз дольше. Проверка на сервере при этом не дорожает.Платит за каждую попытку
СоединениеОтпечаток TLS (JA4) и HTTP/2 в ALPN — только в сети BotOff.Скрипт, назвавшийся браузером, видно сразу
ЗаголовкиClient Hints (версия, платформа, мобильный) против User-Agent, служебные Sec-Fetch-*.«Chrome» без заголовков Chrome
ОкружениеПризнаки автоматизации, безголового браузера, отрисовки без видеокарты, «телефона» без сенсорного экрана. Наружу — только флажки «да/нет».Puppeteer, Playwright, Selenium
ПоведениеМодель в браузере: дрожание руки, ритм печати, удержание клавиш, клики не точно в центр, события от скрипта. Наружу — только оценка 0–100.Ровные линии и печать «метрономом»
ЛовушкиСкрытая ссылка, форма-приманка, обращения к .env, .git и адресам чужих движков.Подсеть на сутки получает задачу дороже — на этом сайте и во всей сети
ТокенПодпись, одноразовость, 2 минуты, привязка к сессии, форме и сайту.Перепродать или повторить нельзя
Клавиатура, экранный диктор, автозаполнение и телефоны не штрафуются: если движений мыши нет, поведение просто не оценивается. А поисковики — настоящие Google и Яндекс, проверенные по DNS, — в ловушки не попадают.

Фильтр содержимого: живой спам, который капча не видит

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

ПравилоЧто делаетПо умолчанию
Одноразовые почтыОтклоняет temp-mail, mailinator, 10minutemail и ещё ~200 сервисов, включая их поддомены. Можно добавить свои домены.включено
СсылкиБольше N ссылок в тексте — отказ. Ссылка в поле «Имя» — отказ всегда: так пишут только рекламу.не больше 3
Стоп-словаВаш список слов и выражений, можно регулярные выражения: /вы\s+выиграли/.пусто
Скорость набораБыстрее N символов в секунду печатает только скрипт. Автозаполнение и вставку из буфера не оцениваем.25 знаков/с
Белый и чёрный спискиIP и подсети, почты и домены. Белый список IP — без всякой проверки (офис, свой сервер), чёрный — отказ даже с верным токеном.пусто

Где настроить: Битрикс — Настройки → Настройки модулей → BotOff → «Фильтр содержимого». PHP-пакет — кабинет → сайт → «Настройки защиты», или ключи max_links, stop_words, deny_ips и другие в конфиге (справочник ниже).

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

Страны и файрвол на уровне сайта

Файрвол срабатывает на любом запросе к сайту, раньше самого сайта. Страна определяется на вашем сервере по свободной базе DB-IP: файл скачивается с botoff.app, только если вы указали страны, и обновляется раз в месяц.

РежимЧто происходит
ВыключенПравил по странам нет.
Только формы (по умолчанию)Из запрещённых стран нельзя отправить форму. Сайт открывается всем — это безопасно для SEO и рекламы.
Весь сайтЗапрещённым странам и чёрному списку IP — 403 на любой странице. Странам из списка «Проверять браузер» и подсетям, попавшим в ловушки, — страница «Проверяем, что вы человек» на секунду-две, после неё сутки без вопросов.
Что файрвол не трогает никогда: поисковых роботов (Яндекс, Google — проверка по обратному DNS), админку, уведомления платёжных систем, обмен с 1С, сам /botoff/ и белый список IP. В режиме наблюдения он только записывает в отчёт. Здесь обычно и ломаются облачные «спам-файрволы»: блокируют поисковики, мобильных операторов и вебхуки оплат — у BotOff это исключено с первой минуты.

Где настроить: Битрикс — вкладка «Файрвол» в настройках модуля. PHP-пакет — «Настройки защиты» в кабинете, или ключи firewall, deny_countries, challenge_countries в конфиге.

Страна по IP: «IP Geolocation by DB-IP» (db-ip.com), лицензия CC BY 4.0.

AJAX и одностраничные сайты

Обычно ничего делать не нужно. Виджет сам добавляет токен в заголовок X-BotOff-Token у запросов fetch и XMLHttpRequest (а значит, и jQuery, axios, BX.ajax), если это POST на ваш же сайт с телом формы (FormData, URLSearchParams, строка a=1&b=2) и адрес не в исключениях. GET-запросы, чужие домены и счётчики аналитики виджет не трогает.

JSON-запросы проверяются только на адресах, которые вы перечислили, — в Битриксе поле «Проверять и JSON-запросы на адресах», в конфиге:

botoff-config.php
'protect_json' => ['#^/ajax/api\.php#', '#^/api/order#'],

Виджет получит этот список и сам добавит токен в такие запросы.

Если нужен ручной контроль

Передать токен самому — например, когда запрос уходит на другой ваш домен:

fetch
const token = await BotOff.getToken('feedback');
await fetch('/ajax/feedback.php', {
  method: 'POST',
  headers: { 'X-BotOff-Token': token },
  body: new FormData(form),
});
jQuery
BotOff.getToken('order').then(function (token) {
  $.ajax({
    url: '/ajax/order.php',
    method: 'POST',
    headers: { 'X-BotOff-Token': token },
    data: $(form).serialize(),
  });
});
axios
const token = await BotOff.getToken('login');
await axios.post('/api/login', formData, { headers: { 'X-BotOff-Token': token } });
Действие в getToken() должно совпадать с тем, что сервер ждёт на этом адресе (routes). Если адрес не указан в таблице — используйте 'form'.
Перехват можно выключить атрибутом data-intercept="off" у тега виджета — тогда токен в AJAX-запросы добавляете сами, как выше.

Чтобы токен был готов заранее (без паузы при отправке), прогрейте его, когда пользователь начал заполнять форму:

JS
input.addEventListener('focus', () => BotOff.prefetch('login'), { once: true });

Для React, Vue и других SPA, где формы создаются скриптом и отправляются через fetch, можно выключить автоматический поиск форм и брать токен вручную:

HTML
<script src="/botoff/botoff.js" data-endpoint="/botoff" data-auto="off" defer></script>

Проверка в своём коде

Если автозащита вам не подходит или нужно проверить обработчик, который она не видит (JSON, REST), проверяйте токен сами:

PHP
<?php
require_once $_SERVER['DOCUMENT_ROOT'] . '/botoff/bootstrap.php';

$check = \BotOff\BotOff::boot()->verify('login');   // токен берётся из поля botoff-token или заголовка X-BotOff-Token
if (!$check['ok']) {
    http_response_code(403);
    exit('Проверка не пройдена: ' . $check['error']);
}
// ... дальше обычная обработка формы

Токен можно передать явно — например, из JSON-тела:

PHP
$data = json_decode(file_get_contents('php://input'), true);
$check = \BotOff\BotOff::boot()->verify('feedback', $data['botoffToken'] ?? '');

Что возвращает verify():

результат
// прошёл
['ok' => true, 'risk' => 10, 'mode' => 'lite', 'reasons' => ['lite'], 'action' => 'login', 'iat' => 1792332000]

// не прошёл
['ok' => false, 'error' => 'replayed']
['ok' => false, 'error' => 'risk_too_high', 'risk' => 85, 'reasons' => ['lite', 'automation'], ...]

Битрикс: в обработчике, который в исключениях

Модуль и так проверяет все формы до компонентов. Ручная проверка нужна для адресов из списка «Не проверять» — например, своего AJAX-обработчика с JSON:

component.php / ajax.php
use Bitrix\Main\Loader;

if (Loader::includeModule('botoff.captcha')) {
    $check = \BotOff\Bitrix\Settings::boot()->verify('order');
    if (!$check['ok']) {
        $this->arResult['ERRORS'][] = 'Не удалось подтвердить, что вы не робот. Обновите страницу.';
        return;
    }
}

Laravel: middleware

Laravel
// app/Http/Middleware/BotOffCheck.php
public function handle($request, Closure $next, string $action = 'form')
{
    require_once public_path('botoff/bootstrap.php');
    $check = \BotOff\BotOff::boot(base_path('botoff-config.php'))->verify($action, $request->input('botoff-token') ?? $request->header('X-BotOff-Token'));
    abort_unless($check['ok'], 403, 'Проверка не пройдена');
    return $next($request);
}

// routes/web.php
Route::post('/login', [AuthController::class, 'login'])->middleware('botoff:login');
Не проверяйте один токен дважды: после первой проверки он считается использованным, вторая вернёт replayed. Если стоит автозащита — она уже проверила токен до вашего кода, и повторно его проверять не нужно.

Переезд с reCAPTCHA и Яндекс SmartCaptcha

Проще всего — убрать старую капчу и поставить автозащиту: формы будут проверяться без правок обработчиков. Если хотите оставить свой код проверки, BotOff отвечает в том же формате, что и они.

  1. Уберите старый виджет со страниц (скрипт recaptcha/api.js или smartcaptcha.yandexcloud.net и блок капчи в форме) и поставьте тег BotOff.
  2. Узнайте локальный секрет — один раз, из консоли или временного скрипта:
    консоль, из корня сайта
    php -r 'require "botoff/bootstrap.php"; echo \BotOff\BotOff::boot()->localSecret(), "\n";'
  3. Поменяйте адрес проверки, секрет и имя поля в обработчике:
    БылоСтало
    Адресhttps://www.google.com/recaptcha/api/siteverify
    https://smartcaptcha.yandexcloud.net/validate
    https://ваш-сайт/botoff/index.php?r=siteverify
    Секретключ из консоли Google / Яндексарезультат шага 2
    Поле с токеномg-recaptcha-response / smart-tokenbotoff-token
PHP, как раньше с reCAPTCHA
$resp = file_get_contents('https://site.ru/botoff/index.php?r=siteverify', false, stream_context_create(['http' => [
    'method' => 'POST',
    'header' => 'Content-Type: application/x-www-form-urlencoded',
    'content' => http_build_query([
        'secret' => BOTOFF_SECRET,
        'response' => $_POST['botoff-token'] ?? '',
        'remoteip' => $_SERVER['REMOTE_ADDR'],
    ]),
]]));
$r = json_decode($resp, true);
if (!$r['success']) { /* отказ */ }

Ответ подходит и под проверку reCAPTCHA (success, score), и под SmartCaptcha (status):

ответ
{"success":true,"status":"ok","challenge_ts":"2026-10-18T12:00:10Z","hostname":"site.ru","action":"form","score":0.9,"error-codes":[]}
Этот путь делает лишний HTTP-запрос к своему же сайту, а некоторые хостинги их запрещают. Надёжнее заменить проверку одной строкой verify() (как) — это и быстрее.

Все настройки

Если сайт подключён к кабинету и там сохранены настройки, то mode, routes, exclude, protect_json, require_token, lite_rate и ip_header берутся из кабинета, а файл нужен только для ключей. Конфиг — обычный PHP-файл, который возвращает массив: botoff-config.php на уровень выше корня сайта (в Битриксе им управляет модуль). Создаётся сам при первом запуске; указывать нужно только то, что меняете. Другой путь к конфигу задаётся переменной окружения BOTOFF_CONFIG или аргументом BotOff::boot('/путь/к/конфигу.php').

КлючПо умолчаниюЧто это
lite_keyсоздаётся самСекретный ключ сайта, 64 hex-символа. Никому не передавайте; при смене все выданные токены станут недействительны.
mode'observe''observe' — наблюдение, 'enforce' — защита.
observe_until0Unix-время, до которого наблюдать; потом защита включится сама. 0 — наблюдать, пока не переключите вручную.
routes[]Регулярное выражение пути → действие. Пример в разделе формы.
default_action'form'Действие для адресов, которых нет в routes.
actionsсм. профилиДействие → профиль (forms, shop, auth, strict). Свои действия добавляйте сюда. Как и exclude, заменяет список целиком — перечислите и стандартные.
excludeплатёжки, 1С, админкиРегулярные выражения путей, которые автозащита не трогает. Заменяет список целиком — см. пример ниже.
protect_json[]Регулярные выражения путей, где проверять и JSON-запросы (токен в заголовке X-BotOff-Token).
require_tokentrueОтклонять формы без токена (у посетителя выключен JavaScript).
originsтекущий доменС каких адресов разрешены формы, напр. ['https://site.ru', 'https://www.site.ru']. Нужен, если сайт открывается на нескольких доменах.
lite_rate60Задач в минуту на подсеть /24 (IPv4) или /56 (IPv6).
content, block_disposabletrueФильтр содержимого и отказ одноразовым почтам (подробно).
max_links, min_typing_cps3, 25Лимит ссылок в тексте и скорость набора, знаков в секунду. 0 — не проверять.
stop_words, disposable_extra[]Стоп-слова (подстрока или /регулярка/) и свои одноразовые домены.
allow_ips, deny_ips[]IP и подсети: без проверки / всегда отказ. ['203.0.113.10', '198.51.100.0/24']
allow_emails, deny_emails[]Почты: адрес, @домен или домен.
firewall'forms''off', 'forms' или 'site' (подробно).
deny_countries, challenge_countries[]Коды стран ISO: ['CN', 'VN']. Проверка браузера — только в режиме site.
fw_trappedtrueВ режиме site — проверка браузера для подсетей из ловушек.
ip_header''Откуда брать IP посетителя за прокси: 'HTTP_X_REAL_IP', 'HTTP_X_FORWARDED_FOR', 'HTTP_CF_CONNECTING_IP' (Cloudflare).
session_cookie'PHPSESSID'Cookie сессии, к которой привязывается токен.
data_dirрядом с конфигомКаталог журнала и служебных файлов. Должен быть доступен PHP на запись и закрыт от веба.
site_key, site_secretпустоКлючи сайта из кабинета: сеть BotOff, отчёт и настройки в кабинете. Адреса узлов и корневые ключи сети пакет знает сам; nodes, root_keys, revoked задавайте, только если вам их выдали отдельно.

Пример конфига интернет-магазина за Cloudflare:

botoff-config.php
<?php
return [
    'lite_key' => '…останется тот, что создался…',
    'mode' => 'enforce',
    'origins' => ['https://shop.ru', 'https://www.shop.ru'],
    'ip_header' => 'HTTP_CF_CONNECTING_IP',
    'routes' => [
        '#^/login#' => 'login',
        '#^/register#' => 'signup',
        '#^/checkout#' => 'order',
        '#^/reviews/add#' => 'review',
    ],
    // свои исключения + стандартные (иначе стандартные пропадут)
    'exclude' => array_merge(\BotOff\Config::defaultExclude(), [
        '#^/api/#',
        '#^/ajax/calc\.php#',
    ]),
];

Справочник виджета

Атрибуты тега <script>

АтрибутПо умолчаниюЧто это
data-endpoint/botoffГде лежит папка BotOff на сайте.
data-actionformДействие по умолчанию для форм и getToken() без аргумента.
data-autoвключеноoff — не искать формы автоматически, только getToken().
data-interceptвключеноoff — не добавлять токен в fetch и XMLHttpRequest автоматически.
data-skip, data-jsonиз конфигаИсключения и JSON-адреса. Ставит scriptTag() сам.
data-routesиз конфигаТаблица адрес → действие. Ставит scriptTag() сам.
data-bindиз сессииПривязка к сессии. Ставит scriptTag() сам.
data-worker-urlрядом со скриптомСвой адрес botoff-worker.js, если файлы лежат на CDN.

Атрибуты форм

data-botoff-action="login"Действие этой формы.
data-botoff="off"Не трогать эту форму.

JavaScript

BotOff.getToken(action?)Промис с одноразовым токеном. Если токен заготовлен — отдаёт сразу.
BotOff.prefetch(action?)Заготовить токен заранее.
BotOff.protect(form)Подключить защиту к форме вручную (при data-auto="off").
BotOff.versionВерсия виджета.

Пока токен получается, у формы есть класс botoff-pending и aria-busy="true" — можно показать индикатор:

CSS
form.botoff-pending button[type=submit] { opacity: .6; pointer-events: none; }

Коды ошибок

КодЧто значитЧто делать
missing_tokenФорма пришла без токенаБот — всё правильно. Если это ваша форма: виджет не подключён на странице или запрос идёт не через <form> → исключение или токен в заголовке.
honeypotЗаполнено поле-ловушкаБот.
replayedТокен уже использованБот или двойная проверка в вашем коде (см. примечание).
expiredТокен старше 2 минутОбычно — страница долго была открыта; виджет сам берёт свежий. Проверьте, что часы сервера идут верно.
wrong_actionТокен выдан для другой формыПоправьте routes или data-botoff-action.
wrong_originТокен получен на другом доменеСайт на нескольких доменах — перечислите их в origins.
bind_mismatchТокен из другой сессииТокен украли или перепродали. Если массово у людей — проверьте session_cookie.
risk_too_highПризнаки автоматизацииБот на настоящем браузере. Причины — в поле reasons.
mode_not_allowedУпрощённая проверка запрещена для этой формыПоявится, когда заработает сеть узлов.
bad_signature, malformedПоддельный или повреждённый токенБот. Если после смены lite_key — пройдёт само.
origin_mismatchЗадачу запросили с чужого домена (в ответе /botoff/)Добавьте домен в origins.
rate_limitedСлишком много задач с одной подсетиАтака — или поднимите lite_rate, если у вас много посетителей из одной сети.

Вопросы

Что увидит посетитель?

Ничего. Задача решается в фоне, пока человек заполняет форму, обычно за 0,3–1 секунды. Если он успел нажать «Отправить» раньше — форма уйдёт через мгновение, сама.

А если у посетителя выключен JavaScript?

Без JavaScript токен не получить. По умолчанию такая форма в режиме защиты не пройдёт (почти все боты — без JavaScript). Если для вас важны такие посетители, поставьте 'require_token' => false — тогда они пройдут, но и простые боты тоже.

Это законно с точки зрения 152-ФЗ и GDPR?

BotOff в режиме Lite не передаёт никаких данных за пределы вашего сервера. В журнал пишутся адрес формы, решение и имена полей — без значений, без IP-адресов в открытом виде.

Что написать в политике конфиденциальности своего сайта?

Ничего особенного: BotOff не собирает и не хранит персональные данные ваших посетителей — ни в Lite, ни в сети. Достаточно упомянуть защиту форм. Пример абзаца:

Для защиты форм сайта от автоматизированных отправок (ботов) используется сервис BotOff. Сервис не собирает и не хранит персональные данные посетителей: проверка выполняется в браузере, а сайт получает только одноразовую оценку риска.
Удобно ли людям с ограниченными возможностями?

Да: нет картинок и головоломок, ничего не нужно видеть или слышать. Пользователи экранных дикторов и клавиатуры не получают штрафов за «нечеловеческое» поведение.

Нагружает ли это сервер?

Почти нет. Выдать задачу — доли миллисекунды, проверить решение — около 40 мс на одну отправку формы, проверить токен — меньше 2 мс. Тяжёлая часть работы — у браузера посетителя.

Не навредит ли это SEO?

Нет. Поисковые роботы (Яндекс, Google) только читают страницы — GET-запросы, а BotOff проверяет лишь отправку форм (POST). Робот получает ту же страницу, что и человек, с тем же кодом ответа, без задержек и без подмены содержимого. Служебные адреса /botoff/ отдают заголовок X-Robots-Tag: noindex и в индекс не попадут. Виджет весит около 20 КБ в сжатом виде, грузится с defer и не мешает отрисовке; тяжёлая часть (решение задачи) запускается, только когда человек начал заполнять форму.

Боты точно не пройдут?

Ни одна капча не даёт гарантии: против живых людей, которым платят за решение, защиты нет ни у кого. BotOff делает массовую атаку дорогой: каждая попытка стоит памяти и процессора, токен одноразовый, живёт 2 минуты и привязан к сессии, форме и сайту. Простые боты не проходят вовсе.

Не навредят ли ловушки SEO?

Нет. Скрытая ссылка помечена rel="nofollow noindex", а её адрес отвечает 404 с X-Robots-Tag: noindex — в индекс он не попадёт. Настоящие роботы Google, Яндекса, Bing, Apple и Mail.ru проверяются по обратному DNS и в ловушку не засчитываются.

Что, если бот и живой человек сидят за одним адресом мобильного оператора?

Ловушки и подозрения не блокируют, а повышают сложность задачи. Человек из той же подсети пройдёт проверку, просто подождёт на секунду-другую дольше. Пометка живёт сутки.

Как узнать, что на сайт напали?

В кабинете включите оповещения: письмо придёт, когда за час отказов больше выбранного порога, — не чаще раза в 6 часов на сайт. Если сайт в режиме наблюдения, письмо подскажет включить защиту.

Чем BotOff отличается от облачных антиспам-модулей?

Проверка идёт на вашем сервере, а не в чужом облаке, поэтому формы работают, даже если наши серверы недоступны. Ни имена, ни почты, ни тексты сообщений, ни IP-адреса посетителей к нам не уходят. Вебхуки платёжных систем, обмен с 1С и админка с первой минуты в исключениях, а первую неделю BotOff только наблюдает — заказы и оплаты не потеряются.

Сайт за Cloudflare или другим прокси

Укажите ip_header, иначе BotOff будет видеть всех посетителей с адреса прокси и сработает ограничение частоты. Для Cloudflare — 'HTTP_CF_CONNECTING_IP'.

Сайт на нескольких доменах

Перечислите все в origins: ['https://site.ru', 'https://www.site.ru', 'https://site.com']. Токен, полученный на одном домене, на другом не подойдёт — так задумано.

Можно ли на Тильду, Wix, конструкторы?

Пока нет: формы там уходят на серверы конструктора, а не на ваш, и проверить токен негде. Для Тильды готовим вариант через вебхук.

Что будет, если ваши серверы упадут?

Сейчас — ничего: в режиме Lite всё работает на вашем сервере, мы не участвуем. Когда появится сеть узлов, при её недоступности сайт сам переключится обратно на Lite, формы продолжат работать.

Удаление

Битрикс

Marketplace → Установленные решения → BotOff → Удалить. Можно оставить журнал в базе (галочка). Модуль уберёт обработчики, агент, папку /botoff/, конфиг и служебные данные. Затем удалите /local/modules/botoff.captcha/.

PHP-пакет

  1. Уберите строку auto_prepend_file (или require в коде) и подождите 5 минут, если это был .user.ini.
  2. Уберите тег виджета из шаблона.
  3. Удалите папку botoff/, файл botoff-config.php и папку botoff-data/.
Порядок важен: сначала автозащита, потом папка. Если удалить папку раньше, PHP не найдёт guard.php из auto_prepend_file и сайт ответит ошибкой.

Вход в кабинет

Без пароля: пришлём код на почту. Первый вход создаст кабинет.

Получая код, вы принимаете соглашение и политику обработки данных. Вход защищён BotOff — без капчи.

Введите код

Отправили 6 цифр на . Загляните и в «Спам».

Готово!

Открываем кабинет…