Защита сайта от сканирования ботами

Открываешь access.log — а там постукивание. Размеренное. Упорное. Механическое, точно дятел
на числовом программном управлении.
/wp-login.php. /.env. /.git/config. /phpmyadmin/.
Сайт, к слову сказать, работает на Hugo. WordPress здесь отродясь не водился. Ни единого дня.
А они всё долбятся. Прошло сорок секунд — и заново. Круглосуточно. Без перерывов, без праздников, без совести.
И нет. Это вовсе не взлом.
Это опись имущества. Обход с блокнотом в руках: что у вас тут ценненького припрятано?
Монотонный стук в дверь
Сканирование — фоновый гул интернета, как шум работающего холодильника: покуда не прислушаешься, его словно бы и нет.
Домен промелькнул в CT-логах в момент выпуска сертификата. Затем отметился в пассивном DNS. Потом кто-то запустил Shodan по вашему диапазону — и готово, приехали: вы уже в списке.
Персонально вас никто не выбирал, не тешьте себя. Просто комбайн катится вдоль всего диапазона адресов и дёргает подряд каждую дверную ручку. Ваша дверь — третья по счёту слева. Ничуть не интереснее соседской.
Загляните в собственные логи. Прямо сейчас. Честное слово, отвлекитесь на минутку:
grep -aoE '"(GET|POST) [^ ]+' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -30
Готов биться об заклад — в топе всплывёт пара позиций из этого джентльменского набора:
/.env,/.git/config,/config.json— секреты, выложенные кем-то наружу по недосмотру. Жанровая классика;/wp-login.php,/xmlrpc.php,/wp-admin/— извечная охота за WordPress. Даже когда его у вас нет и никогда не бывало;/phpmyadmin/,/adminer.php— панельки, позабытые ещё с 2014-го. Боты, не в пример вам, про них помнят. Обо всех;/backup.sql,/dump.sql.gz,/site.zip— вдруг дамп базы до сих пор валяется в корне. А что, вдруг?/actuator/env,/api/v1/pods— Spring и Kubernetes. Мода не выходит из оборота;- ну и нескончаемый перебор
/admin,/manager,/cgi-bin/со всевозможными суффиксами.
А в это же время работает второй слой. И он куда злее.
Парсеры планомерно выкачивают каталог товаров — целиком, вплоть до последней карточки.
Переборщики молотят форму входа: пять паролей ежесекундно, с трёх сотен адресов, разом.
ИИ-краулеры забирают тексты, за которые вы, кстати сказать, платили настоящему автору.
Фермы нагоняют поведенческие метрики и проедают рекламный бюджет кликами.
Весело всем. Кроме вас.
Страшен не тот, кто дыру нашёл. Страшен тот, кто ищет её непрерывно, бесплатно и без усталости. Найдёт ведь.
Кого же мы ловим
«Бот» — слишком уж расплывчатое слово, вроде «зверя» в зоопарке. Отлавливать их приходится по-разному, а перепутать категории — себе в убыток: прихлопнете не тех, а назавтра сайт тихонько выпадет из выдачи.
Знакомьтесь, вот они, всех по именам:
- Сканеры уязвимостей — перебирают маршруты, разыскивают админки,
.env, эксплойт-URL. Чем вредны: логи завалены мусором, а обнаруженная дыра — уже настоящая беда. Лечится фильтром перед сервером; - Переборщики паролей — таранят
/loginсловарями. Угнанные учётки, база, поставленная на колени. Лечится лимитами частоты и проверками; - Парсеры контента — выгребают каталог и статьи полностью. Кража контента плюс счёт за трафик. Проверка на входе;
- ИИ-краулеры — собирают тексты на обучение моделей. Ваш контент утекает в чужую модель. Отключить в списке известных;
- SEO-краулеры — составляют карту ссылок для конкурентов. Нагрузка, засвеченная структура сайта. Отключить в списке известных;
- Фрод-фермы — клонированные браузеры с сотни адресов. Скликивание объявлений, перекошенная аналитика. Отдать слою защиты;
- Поисковики — индексируют. То есть трудятся на вас. Чинить у них нечего. Их нельзя задевать, пропускаем. Всегда.
Последняя строчка, к слову, и есть самая важная.
Половина историй из серии «включили защиту и потеряли трафик» стартует именно с этого: Googlebot отправили на капчу. Он оскорбился. И не вернулся.
robots.txt — никакой это не забор
robots.txt — учтивая просьба. Именно просьба: не закон, не ограда, не шлагбаум.
Поисковики её соблюдают — им выгодно держать лицо. А сканеру уязвимостей на вашу вежливость… ну, скажем прямо: глубоко наплевать. С самой высокой колокольни.
Хуже того. Возьмите такой файл:
User-agent: *Disallow: /admin-panel-2019/Disallow: /backup/Disallow: /internal-api/
…это не оборона. Это карта. Схема проезда к самому лакомому, вычерченная вашими собственными руками.
«Спасибо, — говорит бот, — весьма любезно с вашей стороны», — и топает строго по адресам.
Правило, если совсем кратко: заносите в robots.txt лишь то, что не жалко показать любому
прохожему. Всё прочее закрывается доступом. Просьбами такое не закрывают. Вообще никак.
Три места, куда ставится фильтр
- В приложении — капча в формах, honeypot-поля. Плюс: понимает контекст операции. Минус: запрос уже добежал до кода, БД и диска;
- На сервере — nginx
limit_req, fail2ban, WAF. Полный контроль, без оплаты. Однако видит лишь собственный IP, а против распределённой атаки практически бесполезен; - На периметре, перед сервером — прокси с проверкой браузера. Мусор не доезжает до origin. Зато трафик течёт через посредника — нужен такой, которому доверяешь.
Серверный слой, к слову, списывать со счетов не стоит — пашет, и пашет честно.
Минимальный джентльменский комплект для nginx выглядит так: ограничиваем частоту запросов к форме входа.
limit_req_zone $binary_remote_addr zone=login:10m rate=5r/m;
location = /login {
limit_req zone=login burst=3 nodelay;
proxy_pass http://backend;
}А сверху — fail2ban, чтобы особенно настырные товарищи улетали в бан прямо на уровне ядра. Насовсем. Ну, поначалу на сутки:
[nginx-scan]
enabled = true
port = http,https
filter = nginx-scan
logpath = /var/log/nginx/access.log
maxretry = 6
findtime = 120
bantime = 86400bantime 86400 — это, на минуточку, сутки. В секундах, ага.
Работает? Ещё как, никуда не денется.
Ровно до того дня, когда те же самые запросы посыплются с четырёх тысяч адресов — по одному-два с каждого. Каждый тише воды. Каждый ниже всякого порога, хоть до нуля их опусти.
А сервер лежит. И вы стоите рядом в полном недоумении.
Вот тут серверный слой и завершается. Дальше стартует периметр.
Разбирать станем на примере WebShield: домен переезжает на их NS, трафик идёт сквозь их прокси, а сервер получает уже отфильтрованное. Код сайта при этом не трогаем вовсе. Ни строки. Ноль правок.
Подключение к WebShield
Шаг 1. Переводим домен на их NS сервера
Регистрируетесь, открываете Домены → Добавить домен, вписываете имя в чистом виде, без
https:// и прочих путей: просто example.com.
Затем идёте к регистратору — и меняете NS-серверы на nsbox.webshield.pro и nshub.webshield.pro.
Прежние NS удаляйте. Целиком. Вот прямо все.
Оставите один «на всякий случай» — и получите то самое «работает через раз»: половина резолверов исправно обходит защиту стороной. Именно стороной, да. Такой вот подарочек.
Расползается обновление, как правило, за 10–60 минут. Порой, случается, растягивается и до 48 часов. Подогнать невозможно — DNS живёт собственной жизнью, не спрашивайте.
Проверяем из терминала: dig +short NS example.com, а если хочется взглянуть глазами самого
Google — dig +short NS example.com @8.8.8.8.
Увидели в ответе пару nsbox/nshub — жмите Проверить в кабинете. Статус должен смениться
на Делегирован. Должен, однако не всегда мгновенно: DNS, как мы уже выяснили, соображает
медленно.
Шаг 2. Записи и тумблер Proxy
В разделе DNS создаёте привычные записи: A или AAAA для @, CNAME для www. Обитает
на домене почта — добавляете MX и TXT.
Поддерживаются A, AAAA, CNAME, MX, TXT, SRV, CAA, а SOA и NS — служебные, их
вручную не трогают.
А теперь — ключевой ход всей статьи. У записей A, AAAA и CNAME включается переключатель
Proxy.
Один щелчок — и запросы посетителей летят не к вам напрямую, а через узлы WebShield: с кешированием, WAF и фильтрацией.
Служебные записи (MX, TXT, SRV, CAA) не проксируются по определению, и слава богу: почте
прокси ни к чему.
Дальше — Хосты, там уже настраивается поведение конкретного имени:
- Режим хоста — проксирование либо 301-редирект на иной хост. Удобно для
www, когда сайт обитает на апексе; - SSL required и Redirect HTTP to HTTPS — сертификат выпускается и продлевается самостоятельно, даром, плюс поддомены в комплекте;
- HTTP/2 и HTTP/3 — для клиентских подключений. HTTP/3 браузер подхватывает из заголовка
Alt-Svcсо следующего запроса; если в сети посетителя придушен UDP — не беда, он тихонько останется на HTTP/2; - Протокол до вашего сервера — отрезок «WebShield → origin». Оставьте Автоматически. Серьёзно, не трогайте.
Насчёт последнего пункта — кратко и по существу. Форсируете HTTP/2 на сервер, который его не поддерживает, — ловите 502. На весь сайт целиком. Отката не предусмотрено. Никакого, вообще.
Колеблетесь — не лезьте. Ну прямо инструкция к электроприбору, ей-богу.
Шаг 3. Прячем origin
Самая обидная ошибка — и самая распространённая: защита работает, дашборды сияют, лепота, а атака идёт мимо. Напрямую на IP сервера.
Отчего? Да оттого, что этот IP всё так же торчит наружу, будто антенна.
Проверьте три пункта. Ровно три, не более:
- в DNS не осталось записей на настоящий адрес. Старые поддомены,
mail.,ftp.,old.,dev.и прочие приветы из минувшего — вычищаем всё; - IP уже успел где-то засветиться? История DNS, к вашему сведению, хранится годами. Меняйте IP у хостера, не стесняйтесь;
- на сервере закройте 80 и 443 для всех, кроме узлов WebShield. Для всех без исключения. Кроме них.
На ufw это выглядит следующим образом:
ufw default deny incomingufw allow 22/tcpufw allow from <адрес-узла-WebShield> to any port 443 proto tcpufw enable
Список адресов узлов берите свежий — в кабинете либо у поддержки. Вбивать наугад тут нечего, только поссоритесь с фаерволом попусту.
Шаг 4. Проверка браузера: Off, Browser или Captcha
Вот оно — ядро антисканерной защиты, сердцевина всей затеи.
На каждом хосте выбирается уровень: для проксируемого сайта — в Хостах, для статики — в карточке сайта в разделе Сайты. Режимов всего три, вот они:
- Off — лишних проверок нет, базовая фильтрация сохраняется. Стоит по умолчанию, подходит спокойным сайтам;
- Browser — подозрительных клиентов прогоняют через проверку браузера. Рабочий вариант для большинства случаев, берите его;
- Captcha — проверка с капчей, пожёстче. С Browser взаимоисключается. Это режим на случай активной атаки, нашествия парсеров и скликивания.
Проверка браузера — короткий автоматический экзамен на входе.
Живой человек его, как правило, даже не замечает: страница открылась сама, кликать не потребовалось, поехали. А вот автоматический клиент, лишь прикидывающийся браузером, тест проваливает — и до сервера не доезжает.
Что именно и как там проверяется — знать не обязательно. Ни вам. Ни, тем более, тому, кто в этот экран упрётся. Пусть строит догадки.
А фильтрация вредоносных запросов трудится постоянно, что бы вы ни выставили переключателем: инъекции, служебные файлы, эксплойт-пути — всё отсекается даже на Off.
Сканер, дёргающий /.env, отлетит при любом режиме. Без вариантов.
Как понять, что настала пора ужесточаться? Элементарно: на графике «Атаки во времени» регулярные всплески либо карточка «Прямые заходы на проверке» ползёт вверх — всё, включайте капчу. Она для этого и существует.
Шаг 5. Исключения — точечно, по одному
Проверка браузера ломает всё, что не способно быть браузером: вебсокеты, обращения к API, мобильные и серверные SDK. Им проверка без надобности — они и так свои.
Для таких ребят в настройках хоста предусмотрены исключения. Построчно, примерно в таком духе:
/robots.txt, /sitemap*.xml, /api/*, /ws.
Правила суровые. И это, как ни удивительно, даже к лучшему:
- не больше 50 штук;
- каждое стартует с
/— строго; - корень
/указывать запрещено. Иначе весь смысл пропадает целиком, ну честно же; ..— под запретом;- до 200 символов: буквы, цифры, а также
/,_,.,-,*; - звёздочка
*срабатывает маской в конце сегмента. Лишь там.
Исключение убирает экран проверки. И только его. Фильтрация вредоносных запросов, частотные лимиты и баны на этих путях продолжают работать как ни в чём не бывало.
И всё же — не выносите в исключения формы входа, админку и разделы с данными пользователей. Соблазн велик, ага. Цена промаха — ещё выше.
Шаг 6. Известные боты: кому открыто, а кому 403
Отдельный слой. И, честно говоря, мой любимый.
WebShield опознаёт известных ботов в лицо и позволяет решать за каждого по отдельности — прямо на хосте, в блоке Известные боты. По умолчанию дозволены все. Галочка стоит — пропускаем. Сняли — получают 403, и не где-нибудь, а ещё на границе, не добравшись до вашего сервера.
Распределены они по группам, вот так:
- ИИ-краулеры для обучения — GPTBot, ClaudeBot и anthropic-ai, CCBot, Bytespider, Amazonbot, Meta-ExternalAgent, Cohere, Diffbot. И вся компания;
- ИИ-поисковые краулеры — OAI-SearchBot, PerplexityBot;
- ИИ-ассистенты — ChatGPT-User, Claude-User, Perplexity-User, DuckAssistBot. Эти подтягивают страницу, когда её запросил живой человек в чате — им вроде как даже приятно помогать;
- Поисковые системы — Googlebot, Bingbot, YandexBot, Applebot, DuckDuckBot и прочие;
- Соцсети и мессенджеры — превью ссылок: Telegram, VK, X, LinkedIn, WhatsApp, Discord;
- SEO и анализ — AhrefsBot, SemrushBot, MJ12bot, DotBot, DataForSeoBot;
- Мониторинг — UptimeRobot, Pingdom, StatusCake, Site24x7.
Типичный расклад для контентного проекта: снимаем галочки у обеих ИИ-групп целиком, поисковиков не касаемся.
Индексация живёт, тексты на обучение не утекают.
Ассистенты — на ваше усмотрение: за каждым из них сидит живой человек, и он прямо сейчас читает о вас. Ну или спросил о вас — тоже приятно.
Продавцов ссылок и конкурентские краулеры (AhrefsBot, SemrushBot, MJ12bot) закрывают из
чистой экономии: выкачивать сайты они умеют. Весьма прилежно, стоит отдать должное.
Результат в статистике заметен невооружённым глазом: на вкладке «Посетители», на графике «Люди и боты», слой закрытой категории за сутки-двое схлопывается. А трафик к origin проседает ровно на её долю. Арифметика, однако.
Слой рассчитан на ботов, честно представляющихся. Крупные компании с этим не шутят — блокировка срабатывает для них безотказно.
А вот замаскированного парсера этим списком не отсечёшь: им занимаются иные механизмы защиты. Слои друг друга не подменяют — они складываются. Как одеяла зимой.
И отдельно, чтобы не путались: Google-Extended и Applebot-Extended — это не боты из списка.
Это директивы для robots.txt, записываются в файле так: User-agent: Google-Extended, а ниже —
Disallow: /.
Шаг 7. Скрипт на сайте — видеть, не показывая экран
Согласны показать проверку посетителям не все. И я вас понимаю: интернет-магазин, на счету каждая секунда, и промежуточный экран между рекламой и корзиной — не «издержки», а прямой убыток. Деньги, выброшенные в трубу.
Компромисс отыскался изящный — скрипт на страницах:
<script src="/webshieldpro/t.js" async></script>.
Ключи отсутствуют. Настроек нет. Отдаётся он с вашего же домена, лишних звеньев в цепочке не появляется.
Зачем он нужен: часть примет посетителя различима только изнутри страницы, а без скрипта эта зона так и пребудет слепой. С ним в статистике проявляется подлинная картина: сколько трафика прибывает из ферм и автоматизированных браузеров. Не «сколько было визитов», а кто это был, собственно.
А вот чего он не делает. Понимать это следует чётко, без иллюзий:
- ничего не блокирует. Лишь пополняет данные, на основе которых защита принимает решения;
- проверку браузера не подменяет: та не пускает, а этот всего лишь делает видимым;
- API у него отсутствует — из вашего кода до него не дотянуться, даже не пробуйте;
- для решений по формам не годится вовсе. Там свой инструмент, о нём ниже.
Собираются технические параметры окружения и обезличенные счётчики. Не собираются: содержимое полей, вводимый текст, пароли, наполнение страницы, история просмотров. Ничего подобного.
А вот сам факт технической обработки данных посетителя отразите в политике конфиденциальности — это зона вашей ответственности, и тут уже никто за вас не подстрахует.
Шаг 8. Формы: отдельная задача, отдельный инструмент
Регистрации, заявки, промокоды, вход — здесь всё про доверие к конкретному действию, а не к трафику в целом.
Инструмент отдельный — Защита форм. В кабинете заводите проект, получаете ключ, вставляете тег
в <head> и перечисляете формы селекторами:
<script src="https://af.webshield.pro/t/v1.js?k=ВАШ_КЛЮЧ" data-forms="#signup, #contact_form" async></script>
И на этом всё, поехали.
Далее ваш бэкенд перед приёмом заявки запрашивает оценку по идентификатору сессии и получает вердикт с причинами.
Вердикт применяет ваш код — затормозить запрос вне потока трафика физически невозможно, и об этом в документации написано честно, без прикрас.
Домен, кстати, можно и вовсе не переводить на WebShield: формы защищаются отдельно. Удобно, если переезд пока не входит в планы.
Смотрим, что вышло
Спустя сутки открывайте Статистику. Вот там — всё самое интересное, обещаю.
Вкладка «Защита». Отражённых атак, заблокированных IP, фейковых поисковых ботов, показанных проверок браузера, прямых заходов на проверке, подозрительных кликов по рекламе. Зелёный ноль в первой карточке — это хорошо. Даже отлично.
Вкладка «Баны». Живой перечень тех, кого система удерживает прямо сейчас. Без суда и следствия, зато с причиной:
- Заблокированные запросы — сколько отсечено за выбранный период;
- Ещё стучится — бот не сдался и продолжает попытки. Упорный;
- Действие — отказ на границе либо отправка на проверку;
- Причина — сканер, всплеск ошибок, подбор входа, бот-ферма;
- Истекает — когда нынешняя блокировка будет снята.
Строка «сканер · Ещё стучится · 14 302 запроса» мирит с жизнью лучше всякой рекламы. Честное слово.
Вот они — четырнадцать с лишним тысяч запросов, которые больше не добрались до вашего PHP. И не доберутся.
Вкладка «Посетители». График «Люди и боты», слои по категориям: люди, поисковики, ИИ-боты, соцсети, SEO, мониторинг, сканеры.
Уникальные посетители здесь — подтверждённые живые браузеры, поэтому цифра заметно ниже, чем в Метрике. И, если честно, заметно ближе к истине. Метрика-то считает всех подряд, включая пылесосы.
Вкладка «Здоровье сайта» → «Поисковые системы». Таблица со статусом обхода: 🟢 обходит без проблем, 🟡 имеются ошибки, 🔴 упирается в защиту, серый — давно не заглядывал.
Заглядывайте сюда после всякого ужесточения настроек. Увидели красный напротив Яндекса или Google — не раздумывайте: смягчайте режим либо добавляйте исключения. Немедленно.
И быстрая проверка вручную, пока вкладка открыта: curl -sI https://example.com/robots.txt | head -1,
и то же самое для https://example.com/sitemap.xml.
Оба обязаны отдать 200. Прилетел взамен экран проверки — поздравляю, вы только что закрыли карту
сайта от поисковика. Классика жанра, увы.
Что остаётся на вашей стороне
Периметр не отменяет гигиену, вот в чём штука. Никакой прокси, пусть даже самый навороченный,
не спасёт от админки с паролем admin123. Ни один:
- обновления CMS, плагинов и тем — по расписанию, а не «когда вспомним». Вспоминаем мы, как правило, уже после взлома;
.env,.git, дампы БД — вне вебрута. Всегда. Даже «на пять минут». Тем более на пять минут;- двухфакторка на админку, а сама админка — не по адресу
/admin, умоляю; - honeypot-поле в формах: скрытое, для человека незримое. Заполнено — значит, отправитель бот, всё просто;
- минимальные права на файлы, отдельный пользователь для приложения;
- бэкапы, которые кто-нибудь хотя бы раз пробовал развернуть. Не «делаем», а именно «развернуть пробовал».
Чего защита не делает
Раздел, которого в рекламных буклетах, как правило, нет. А зря. Вот он:
- Не ловит 100% вредного трафика. И не обещайте себе этого. Ни одна система в мире не ловит. Задача скромнее: снять основной объём и поднять цену атаки настолько, чтобы стало неинтересно;
- Не разбирает содержимое заявок. Ручной спам, сочинённый живым человеком, пройдёт. Ну а чего вы ждали — он и правда человек;
- Не влияет на ранжирование. Ботовую нагрузку, искажающую метрики, снижает. А что засчитает Яндекс или Google в выдаче — их забота, гарантий по позициям нет и быть не может;
- Не возвращает деньги за скликивание. Выявляет подозрительные клики, отдаёт выгрузку IP и времени в качестве доказательства. А возврат оформляет рекламная сеть, решение за ней;
- Не чинит дыру в вашем коде. Периметр сокращает число попыток, но уязвимость от этого никуда не девается. См. раздел выше — он для этого и написан.
Чек-лист
Пробегитесь взглядом, прежде чем закрывать вкладку. Всё отметили — можно спать спокойно:
- домен делегирован,
dig +short NSпоказываетnsbox/nshub, старых NS не осталось — ни единого; Proxyвключён для всехA,AAAA,CNAMEсайта;- настоящий IP сервера нигде не торчит, порты 80/443 открыты только для узлов защиты;
Bot protectionв режимеBrowser. Как минимум;- исключения заданы точечно:
/robots.txt,/sitemap*.xml, API, вебсокеты. Корень — не трогали; - ИИ-краулеры и лишние SEO-боты сняты с галочек, поисковики разрешены;
robots.txtиsitemap.xmlотдают200без экрана проверки;- «Здоровье сайта → Поисковые системы» — зелёный по Яндексу и Google;
- спустя сутки на вкладке «Баны» видно, кого система держит и за что. И это приятно.
Полной тишины в логах не дождётесь, не мечтайте — стучаться не перестанут никогда. Но стучаться теперь они будут не в вашу дверь. В чужую, за квартал отсюда.

