Содержание

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

Открываешь 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 — учтивая просьба. Именно просьба: не закон, не ограда, не шлагбаум.

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

Хуже того. Возьмите такой файл:

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  = 86400

bantime 86400 — это, на минуточку, сутки. В секундах, ага.

Работает? Ещё как, никуда не денется.

Ровно до того дня, когда те же самые запросы посыплются с четырёх тысяч адресов — по одному-два с каждого. Каждый тише воды. Каждый ниже всякого порога, хоть до нуля их опусти.

А сервер лежит. И вы стоите рядом в полном недоумении.

Вот тут серверный слой и завершается. Дальше стартует периметр.

Разбирать станем на примере WebShield: домен переезжает на их 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, как мы уже выяснили, соображает медленно.

Ставьте защиту заранее
Защиту поднимают до атаки, а не в разгар. Пока NS расползаются по резолверам, ботнет продолжает молотить по прежнему адресу. А вы сидите и ждёте. Ждать под обстрелом — удовольствие сомнительное, проверено на себе.

В разделе 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. На весь сайт целиком. Отката не предусмотрено. Никакого, вообще.

Колеблетесь — не лезьте. Ну прямо инструкция к электроприбору, ей-богу.

Самая обидная ошибка — и самая распространённая: защита работает, дашборды сияют, лепота, а атака идёт мимо. Напрямую на IP сервера.

Отчего? Да оттого, что этот IP всё так же торчит наружу, будто антенна.

Проверьте три пункта. Ровно три, не более:

  1. в DNS не осталось записей на настоящий адрес. Старые поддомены, mail., ftp., old., dev. и прочие приветы из минувшего — вычищаем всё;
  2. IP уже успел где-то засветиться? История DNS, к вашему сведению, хранится годами. Меняйте IP у хостера, не стесняйтесь;
  3. на сервере закройте 80 и 443 для всех, кроме узлов WebShield. Для всех без исключения. Кроме них.

На ufw это выглядит следующим образом:

  • ufw default deny incoming
  • ufw allow 22/tcp
  • ufw allow from <адрес-узла-WebShield> to any port 443 proto tcp
  • ufw enable

Список адресов узлов берите свежий — в кабинете либо у поддержки. Вбивать наугад тут нечего, только поссоритесь с фаерволом попусту.

Вот оно — ядро антисканерной защиты, сердцевина всей затеи.

На каждом хосте выбирается уровень: для проксируемого сайта — в Хостах, для статики — в карточке сайта в разделе Сайты. Режимов всего три, вот они:

  • Off — лишних проверок нет, базовая фильтрация сохраняется. Стоит по умолчанию, подходит спокойным сайтам;
  • Browser — подозрительных клиентов прогоняют через проверку браузера. Рабочий вариант для большинства случаев, берите его;
  • Captcha — проверка с капчей, пожёстче. С Browser взаимоисключается. Это режим на случай активной атаки, нашествия парсеров и скликивания.

Проверка браузера — короткий автоматический экзамен на входе.

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

Что именно и как там проверяется — знать не обязательно. Ни вам. Ни, тем более, тому, кто в этот экран упрётся. Пусть строит догадки.

А фильтрация вредоносных запросов трудится постоянно, что бы вы ни выставили переключателем: инъекции, служебные файлы, эксплойт-пути — всё отсекается даже на Off.

Сканер, дёргающий /.env, отлетит при любом режиме. Без вариантов.

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

Проверка браузера ломает всё, что не способно быть браузером: вебсокеты, обращения к API, мобильные и серверные SDK. Им проверка без надобности — они и так свои.

Для таких ребят в настройках хоста предусмотрены исключения. Построчно, примерно в таком духе: /robots.txt, /sitemap*.xml, /api/*, /ws.

Правила суровые. И это, как ни удивительно, даже к лучшему:

  • не больше 50 штук;
  • каждое стартует с / — строго;
  • корень / указывать запрещено. Иначе весь смысл пропадает целиком, ну честно же;
  • .. — под запретом;
  • до 200 символов: буквы, цифры, а также /, _, ., -, *;
  • звёздочка * срабатывает маской в конце сегмента. Лишь там.

Исключение убирает экран проверки. И только его. Фильтрация вредоносных запросов, частотные лимиты и баны на этих путях продолжают работать как ни в чём не бывало.

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

Отдельный слой. И, честно говоря, мой любимый.

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: /.

Согласны показать проверку посетителям не все. И я вас понимаю: интернет-магазин, на счету каждая секунда, и промежуточный экран между рекламой и корзиной — не «издержки», а прямой убыток. Деньги, выброшенные в трубу.

Компромисс отыскался изящный — скрипт на страницах: <script src="/webshieldpro/t.js" async></script>.

Ключи отсутствуют. Настроек нет. Отдаётся он с вашего же домена, лишних звеньев в цепочке не появляется.

Зачем он нужен: часть примет посетителя различима только изнутри страницы, а без скрипта эта зона так и пребудет слепой. С ним в статистике проявляется подлинная картина: сколько трафика прибывает из ферм и автоматизированных браузеров. Не «сколько было визитов», а кто это был, собственно.

А вот чего он не делает. Понимать это следует чётко, без иллюзий:

  • ничего не блокирует. Лишь пополняет данные, на основе которых защита принимает решения;
  • проверку браузера не подменяет: та не пускает, а этот всего лишь делает видимым;
  • API у него отсутствует — из вашего кода до него не дотянуться, даже не пробуйте;
  • для решений по формам не годится вовсе. Там свой инструмент, о нём ниже.

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

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

Регистрации, заявки, промокоды, вход — здесь всё про доверие к конкретному действию, а не к трафику в целом.

Инструмент отдельный — Защита форм. В кабинете заводите проект, получаете ключ, вставляете тег в <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;
  • спустя сутки на вкладке «Баны» видно, кого система держит и за что. И это приятно.

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

Похожее