Стоит запустить новый сайт на VPS — и уже через несколько часов в логах веб-сервера появляются запросы к /wp-login.php, /administrator и /phpmyadmin. Лично к вам это отношения не имеет: автоматические сканеры обходят целые диапазоны адресов и проверяют типовые пути входа. Нашли форму авторизации — начинают подбирать пароли.
Админ-панель открывает доступ к контенту, настройкам, плагинам и нередко к выполнению кода на сервере. Поэтому защита админ-панели входит в обязательный минимум для любого проекта, будь то блог, интернет-магазин или корпоративный портал. На виртуальном сервере всё держится на вас: готовой панели хостинга, которая закрыла бы лишнее, нет, зато есть полный контроль над сетью, веб-сервером и приложением.
В статье разберём, откуда приходят атаки, как собрать несколько независимых уровней защиты и в каком порядке их настраивать. Примеры написаны для Ubuntu, Nginx и WordPress, но принципы работают и для других CMS, и для самописных админок. Звучит громоздко? На практике базовая настройка занимает около часа.
Что такое админ-панель и почему её атакуют
Админ-панель — это веб-интерфейс, через который управляют сайтом: публикуют материалы, ставят плагины, меняют настройки и права пользователей. В WordPress это /wp-admin/, в Joomla — /administrator, в 1С-Битрикс — /bitrix/admin, в Django и Laravel — собственные разделы вроде /admin. Сюда же относятся инструменты управления базами данных, например phpMyAdmin и Adminer. Общая черта у всех одна: страница открыта из интернета, а за единственным паролем стоит полный контроль над проектом.
Что получает человек, который попал внутрь? Выбор большой: публикация спама и фишинговых страниц, внедрение вредоносного JavaScript, установка плагинов с бэкдорами, выгрузка клиентской базы, запуск майнера на вашем сервере. Сайт попадает в чёрные списки браузеров и поисковиков, а восстанавливать репутацию приходится месяцами. Именно поэтому админку атакуют чаще остальных частей сайта.
Чтобы выбирать меры осознанно, полезно понимать, с какими сценариями вы имеете дело. Основные из них собраны в таблице.
| Тип атаки | Как выглядит в логах | Чем закрывается |
|---|---|---|
| Брутфорс (перебор паролей) | Сотни POST-запросов к форме входа с одного или нескольких адресов | Лимит запросов, fail2ban, 2FA |
| Credential stuffing | Вход с парами логин-пароль из чужих утечек | Уникальные пароли, 2FA, белый список адресов |
| Уязвимости плагинов и тем | Запросы к известным путям плагинов, попытки загрузки файлов | Обновления, минимум плагинов, WAF |
| Перехват трафика | Логин и пароль уходят по HTTP открытым текстом | HTTPS, HSTS |
| Перебор имён пользователей | Запросы вида /?author=1 и /wp-json/wp/v2/users |
Отключение выдачи списка пользователей, нестандартный логин |
| Угон сессии | Вход по украденной cookie с другого адреса | Обновления, флаги Secure и HttpOnly, короткое время жизни сессии |
Как работает многоуровневая защита
Идея простая: ни одна мера не закрывает все сценарии, поэтому защиту строят слоями. Если злоумышленник обошёл один уровень, он упирается в следующий. Проще говоря, вы увеличиваете цену атаки, пока она не перестаёт окупаться. Большинство ботов сдаётся на первом же слое, а до остальных доходят единицы.
| Уровень | Что делает | Инструменты |
|---|---|---|
| Сеть | Оставляет открытыми только нужные порты, прячет панель от посторонних | UFW, VPN, SSH-ключи |
| Веб-сервер | Фильтрует запросы до того, как они дойдут до PHP | Nginx: allow/deny, basic auth, limit_req, HTTPS |
| Приложение | Проверяет, кто именно входит и что ему разрешено | 2FA, роли, настройки CMS |
| Наблюдение | Замечает перебор, банит источники, помогает восстановиться | fail2ban, логи, резервные копии |
Настраивать удобнее снаружи внутрь: сначала сеть, затем веб-сервер, потом приложение. Перед любыми правками стоит сделать копию сервера. На VPS Serverspace для этого подойдёт снимок: если новая конфигурация Nginx заблокирует вас самих, откатиться можно быстро. Подробнее о конфигурациях серверов — на странице VPS-серверов Serverspace.
Пошаговая настройка: как защитить админ-панель на VPS
Шаг 1. Обновите систему и включите автоматические обновления безопасности
Большая часть успешных взломов происходит через известные уязвимости, для которых давно вышли патчи. Начните с обновления пакетов:
Затем включите автоматическую установку обновлений безопасности, чтобы не зависеть от собственной памяти:
Об обновлениях самой CMS и плагинов тоже не стоит забывать. Если установлен WP-CLI, всё обновляется одной командой:
Шаг 2. Настройте брандмауэр и защитите SSH
Снаружи должны быть видны только порты 22 (SSH), 80 и 443. Всё остальное, включая порт базы данных 3306, закрывается. В Ubuntu для этого удобно использовать UFW. Важно соблюдать порядок: сначала разрешаем SSH и только потом включаем фильтр, иначе можно потерять доступ к серверу.
Теперь SSH. Вход по ключам надёжнее паролей: в файле /etc/ssh/sshd_config задайте PasswordAuthentication no и PermitRootLogin no, затем перезапустите службу:
Прежде чем закрывать текущую сессию, откройте вторую и убедитесь, что вход по ключу работает. Лишние пять минут сэкономят час возни с консолью восстановления.
Шаг 3. Включите HTTPS
Без шифрования логин и пароль от админки передаются открытым текстом, и любой промежуточный узел может их прочитать. Бесплатные сертификаты Let's Encrypt устанавливаются за пару минут:
Certbot сам добавит редирект с HTTP на HTTPS и настроит автоматическое продление. Проверить, что продление работает, можно так:
Для WordPress дополнительно добавьте в wp-config.php строку define('FORCE_SSL_ADMIN', true);. Она заставит передавать данные входа только по защищённому каналу.
Шаг 4. Разрешите вход только с доверенных адресов
Самая сильная мера из всего списка. Если админка отвечает лишь нескольким адресам, остальной интернет её просто не видит: боты получают ответ 403 и уходят искать другую цель. Доступ по IP настраивается в Nginx через директивы allow и deny внутри нужного блока location.
| Директива | Что делает | Пример |
|---|---|---|
allow |
Разрешает адрес или подсеть | allow 203.0.113.10; |
deny all |
Запрещает всем остальным; ставится после всех allow | deny all; |
location ~ |
Блок по регулярному выражению для страницы входа и PHP-файлов админки | location ~ ^/(wp-login\.php|wp-admin/(?!admin-ajax\.php).*\.php)$ |
Здесь есть тонкость, на которой спотыкаются многие. Запросы к .php-файлам обрабатывает отдельный блок с регулярным выражением, поэтому правила allow и deny в обычном блоке location /wp-admin/ до них просто не дойдут. Надёжнее описать защищаемые пути регулярным выражением из таблицы, поставить этот блок выше основного обработчика PHP и повторить в нём те же include и fastcgi_pass, что стоят в основном блоке. Файл admin-ajax.php исключён намеренно: к нему обращаются и обычные посетители, например формы и корзина.
После правки проверьте конфигурацию и перезагрузите Nginx:
Убедиться в результате можно с любого другого адреса, например с телефона через мобильный интернет, или командой с постороннего сервера. Ожидаемый ответ — 403:
Шаг 5. Добавьте второй пароль на уровне веб-сервера
HTTP Basic Auth спрашивает логин и пароль ещё до того, как запрос попадёт в PHP. Даже если в форме входа или плагине найдут уязвимость, добраться до неё без этого пароля не получится. Создадим файл с учётной записью:
Затем в тот же блок location добавьте директивы auth_basic "Restricted"; и auth_basic_user_file /etc/nginx/.htpasswd;. По умолчанию действует режим satisfy all: пользователь должен пройти и проверку адреса, и ввод пароля. Если нужно разрешить вход либо с доверенного адреса, либо по паролю, добавьте satisfy any;. Пароль для Basic Auth должен отличаться от пароля в самой CMS — иначе смысл второго уровня теряется.
Шаг 6. Ограничьте частоту запросов к странице входа
Перебор паролей — это сотни запросов в минуту, тогда как живой человек вводит пароль раз в несколько секунд. Модуль limit_req отсекает слишком частые обращения до того, как они нагрузят PHP и базу данных. В блоке http (обычно в /etc/nginx/nginx.conf) объявите зону: limit_req_zone $binary_remote_addr zone=login:10m rate=5r/m;. Затем в блоке со страницей входа примените её: limit_req zone=login burst=3 nodelay; и limit_req_status 429;.
Пять запросов в минуту с одного адреса достаточно для обычной работы и мало для бота. Заодно закройте xmlrpc.php: через него можно перебирать пароли пачками, а нужен он лишь некоторым мобильным приложениям и Jetpack. Если вы ими не пользуетесь, добавьте блок location = /xmlrpc.php { deny all; }.
Шаг 7. Подключите fail2ban
Fail2ban читает журналы, находит повторяющиеся неудачные попытки входа и временно блокирует источник на уровне файрвола. Установка занимает минуту:
Для базовой авторизации Nginx в пакете уже есть готовый фильтр nginx-http-auth. Для страницы входа WordPress понадобится собственный. Создайте файл /etc/fail2ban/filter.d/wp-login.conf с секцией [Definition] и строкой failregex = ^<HOST> .* "POST /wp-login\.php[^"]*" 200. Расчёт здесь такой: при неверном пароле WordPress возвращает код 200, а при успешном входе — редирект 302, поэтому удачные входы под правило не попадают. Затем опишите «клетку» в файле /etc/fail2ban/jail.local:
| Параметр | Значение | Зачем нужен |
|---|---|---|
[wp-login] |
имя секции | Название «клетки», по нему вызывают статус и разбан |
enabled |
true | Включает правило |
filter |
wp-login | Подключает созданный фильтр |
logpath |
/var/log/nginx/access.log | Журнал, который разбирает fail2ban |
maxretry |
5 | Сколько неудач допустимо до бана |
findtime |
10m | Окно, в котором считаются попытки |
bantime |
1h | Длительность блокировки |
ignoreip |
ваш адрес | Защищает от бана самого администратора |
Остаётся перезапустить службу и проверить, что правило работает:
Если вы случайно забанили своего сотрудника, снять блокировку можно одной командой:
Шаг 8. Подключите второй фактор входа
Что будет, если пароль всё-таки подобрали или он утёк из другого сервиса? Здесь выручает двухфакторная аутентификация: без одноразового кода из приложения войти не получится, даже зная пароль. Для WordPress подойдут плагины Two Factor, WP 2FA и Wordfence Login Security. Они работают с приложениями-генераторами кодов вроде Google Authenticator, Aegis или 2FAS, а Two Factor поддерживает ещё и аппаратные ключи FIDO2.
Включайте второй фактор для всех, у кого есть права администратора и редактора, и сразу сохраните резервные коды в менеджере паролей. Заодно наведите порядок с учётными записями. Не используйте логин admin, выдавайте роли по минимуму, а бывших сотрудников удаляйте сразу. Список администраторов проверяется командой:
Шаг 9. Уберите лишнее и спрячьте типовые пути
Эти меры не заменяют предыдущие, но снижают шум в логах и убирают очевидные лазейки:
- отключите редактор файлов в админке: добавьте в
wp-config.phpстрокуdefine('DISALLOW_FILE_EDIT', true);, и через панель больше нельзя будет изменить код темы или плагина; - перенесите адрес входа с
/wp-login.phpна нестандартный путь плагином вроде WPS Hide Login, большинство ботов тогда не найдёт форму; - уберите phpMyAdmin из публичного доступа и подключайтесь к нему через SSH-туннель, например командой
ssh -L 8080:127.0.0.1:80 user@203.0.113.5; - ограничьте права на конфигурацию командой
sudo chmod 640 /var/www/example.kz/wp-config.php, чтобы файл с данными базы не читали посторонние процессы.
Шаг 10. Настройте журналы и резервные копии
Защиту нужно проверять. Раз в неделю полезно смотреть, кто чаще всего стучится на страницу входа:
sudo grep "POST /wp-login.php" /var/log/nginx/access.log | awk '{print $1}' | sort | uniq -c | sort -rn | head[/code>
Если в верхних строках сотни запросов с одного адреса, значит, перебор идёт, а лимиты и fail2ban делают свою работу. Дополнительно поставьте плагин журнала действий, чтобы получать уведомления о входах и смене ролей. И главное: резервная копия, которую ни разу не пробовали восстановить, копией не считается. Раз в квартал разворачивайте её на тестовом сервере. Хранить копии стоит отдельно от основного VPS, а перед крупными изменениями удобно делать снимок диска.
Плюсы и минусы каждой меры
Универсального рецепта нет: у каждой защиты своя цена в виде неудобств для команды и времени на поддержку. Сравнение помогает выбрать набор под свой проект.
| Мера | Плюсы | Минусы |
|---|---|---|
| Белый список адресов | Прячет панель от всего интернета, не нагружает сервер | Неудобен при динамическом адресе, есть риск самоблокировки |
| Basic Auth | Срабатывает до PHP, закрывает даже уязвимости формы входа | Ещё один пароль для команды, может мешать плагинам и API |
| Лимит запросов | Гасит перебор на раннем этапе | Требует аккуратной настройки, чтобы не задеть живых пользователей |
| fail2ban | Автоматически банит источники атак | Ложные срабатывания за общим NAT, зависит от формата логов |
| 2FA | Защищает от украденных и подобранных паролей | Нужно обучить команду и хранить резервные коды |
| Смена адреса входа | Убирает основной шум из логов | Целенаправленную атаку не остановит |
| VPN | Панель полностью недоступна снаружи, работает с любого устройства | Нужно поддерживать VPN-сервер и выдавать ключи |
Ограничения и риски
Даже полный набор мер не делает сайт неуязвимым. Самый неприятный случай — уязвимость нулевого дня в плагине, позволяющая обойти авторизацию. Ни пароль, ни второй фактор тут не помогут, потому что атака идёт мимо формы входа. Против такого сценария работают только своевременные обновления, минимум установленных плагинов и WAF, который режет подозрительные запросы по сигнатурам.
Второй риск касается вас самих. Ошибка в списке адресов — и войти не сможет никто, включая администратора. Поэтому перед каждым изменением запускайте nginx -t, держите открытой вторую SSH-сессию и заранее делайте снимок сервера.
Третья ловушка возникает, когда сайт стоит за CDN или обратным прокси. В этом случае Nginx видит адрес прокси, а не посетителя, и правила по адресам перестают работать. Хуже того, fail2ban может забанить сам прокси, и сайт станет недоступен для всех. Решение — настроить модуль real_ip: директивы set_real_ip_from с диапазонами прокси и real_ip_header X-Forwarded-For.
Остальные ограничения проще перечислить коротко. Офис с общим адресом даёт ложные срабатывания fail2ban, и тут выручает ignoreip. Жёсткие правила могут ломать REST API, wp-cron и мобильные приложения, поэтому после каждого изменения проверяйте формы, корзину и поиск. Смена адреса входа создаёт лишь ощущение безопасности, если за ней нет остальных слоёв.
Практические сценарии
Сценарий 1. Корпоративный сайт, команда сидит в офисе
У компании один статический адрес, и редакторов человек пять. Идеальный случай: в белый список попадает адрес офиса, всем остальным отдаётся 403. Сверху добавляют Basic Auth и второй фактор. Получается, что доступ по IP отсекает 99 % ботов, а оставшиеся упираются в два пароля и одноразовый код. Работать с такой настройкой можно годами без обслуживания.
Сценарий 2. Редактор работает из дома и в поездках
Динамические адреса ломают белые списки. Выход — поднять VPN-шлюз с постоянным адресом, например WireGuard на отдельном небольшом VPS:
sudo apt install wireguard -y
Адрес шлюза вносится в список разрешённых, а сотрудники подключаются к VPN перед входом в админку. Для всех остальных панель остаётся закрытой, а сами редакторы получают привычный интерфейс без лишних паролей.
Сценарий 3. Интернет-магазин на WooCommerce с десятком менеджеров
Менеджеры заходят из разных мест, и белый список здесь нереален. Опираются на другие слои: обязательная двухфакторная аутентификация для всех ролей выше покупателя, лимит запросов, fail2ban, роль Shop Manager вместо администратора там, где полных прав не нужно, и журнал действий. Параллельно подключают WAF — он закрывает атаки на платёжные плагины. Магазин получает защиту без неудобств для команды.
Сценарий 4. Самописная админка на Django или Laravel
Здесь удобнее вынести панель на отдельный поддомен, например admin.example.kz, с собственным серверным блоком Nginx, отдельным сертификатом и своими правилами. Основной домен вообще не знает о существовании админки, а значит, сканеры не найдут её по типовым путям. На поддомен накладывают белый список адресов, Basic Auth и ограничение частоты запросов.
Сценарий 5. Сайт ломают прямо сейчас
В логах сотни запросов к форме входа, сайт тормозит. Экстренная защита админ-панели строится по шагам:
- определите самые активные адреса командой из шага 10 и заблокируйте их, например
sudo ufw deny from 203.0.113.50; - включите лимит запросов и fail2ban, если их ещё нет;
- смените пароли всех администраторов и проверьте список ролей на незнакомые учётные записи;
- обновите ключи и соли в
wp-config.php, чтобы завершить все активные сессии; - проверьте недавно изменённые файлы командой
sudo find /var/www/example.kz -type f -mtime -2.
Типичные ошибки
| Ошибка | К чему приводит | Как исправить |
|---|---|---|
| Логин admin и простой пароль | Взлом перебором за несколько часов | Нестандартный логин, менеджер паролей, 2FA |
Правила allow/deny только в блоке location /wp-admin/ |
Блок PHP перехватывает запрос, и панель остаётся открытой для всех | Регулярное выражение выше обработчика PHP, проверка через curl с постороннего адреса |
Закрыт admin-ajax.php |
Перестают работать формы, поиск и корзина | Исключить файл из ограничения |
| Игнорирование CDN или прокси | Fail2ban банит адрес прокси, сайт недоступен | Настроить real_ip |
| UFW включён до разрешения SSH | Потеря доступа к серверу | Сначала allow OpenSSH, при проблемах помогает консоль в панели управления |
| Белый список (доступ по IP) без запасного канала | Адрес сменился, и администратор открывает панель всем «временно» | VPN с постоянным адресом |
| Резервные копии ни разу не восстанавливали | После взлома восстанавливаться не из чего | Тестовое восстановление раз в квартал |
Итоги
Надёжная защита админ-панели на VPS строится из нескольких простых слоёв. Сначала закрываем сеть и включаем HTTPS. Затем ограничиваем вход по адресам и добавляем Basic Auth на уровне Nginx. Поверх ставим лимиты и fail2ban, а в приложении включаем второй фактор и убираем лишние права. Каждая мера сама по себе небольшая, но вместе они превращают лёгкую добычу в слишком дорогую цель.
Если сомневаетесь, с чего начать, возьмите минимальный набор: HTTPS, 2FA, лимит запросов и fail2ban. Он занимает меньше часа и закрывает основную массу автоматических атак. Остальные уровни добавляйте по мере роста проекта. Для тестов и отработки конфигураций удобно развернуть отдельный сервер, например VPS в Serverspace, а материалы по администрированию и безопасности собраны в блоге Serverspace.
FAQ
Достаточно ли просто сменить адрес входа в админку?
Этого мало. Новый адрес убирает основной шум от ботов, но не останавливает тех, кто целенаправленно ищет вашу панель, а адрес часто раскрывается через ссылки, карту сайта или заголовки. Используйте смену пути как дополнение к лимитам, белому списку и второму фактору.
Можно ли обойтись одной двухфакторной аутентификацией?
Для небольшого блога это уже серьёзный шаг, однако полагаться только на неё не стоит. Второй фактор не спасает от уязвимостей в плагинах и не снимает нагрузку от перебора. Лимит запросов и fail2ban остановят сотни ненужных обращений ещё до проверки кода.
Как понять, что админку уже пытаются взломать?
Посмотрите журнал доступа Nginx: частые POST-запросы к /wp-login.php с одних и тех же адресов говорят о переборе. Помогает и плагин журнала действий, который сообщает о неудачных входах. Команда из шага 10 покажет самых активных отправителей за пару секунд.
Что делать, если у меня динамический IP-адрес?
Белый список в таком случае неудобен: адрес меняется, и доступ пропадает. Поднимите VPN с постоянным адресом и разрешите вход только с него. Вариант попроще — вообще не ограничивать панель по адресам, но усилить остальные уровни: лимиты, fail2ban и второй фактор.
Нужен ли WAF, если уже установлен fail2ban?
Это инструменты для разных задач. Fail2ban реагирует на повторяющиеся неудачные попытки, а WAF анализирует содержимое запросов и блокирует эксплуатацию уязвимостей, SQL-инъекции и XSS. Для небольшого сайта хватит плагина с функциями фильтрации, для магазина с оплатой лучше добавить полноценный WAF.
Как часто нужно менять пароли администраторов?
Регулярная смена по календарю приносит меньше пользы, чем длинный уникальный пароль в менеджере паролей. Меняйте его при подозрении на утечку, после ухода сотрудника и после любого инцидента. Вместе со вторым фактором этого достаточно для большинства проектов.
Достаточно ли просто сменить адрес входа в админку?
Этого мало. Новый адрес убирает основной шум от ботов, но не останавливает тех, кто целенаправленно ищет вашу панель, а адрес часто раскрывается через ссылки, карту сайта или заголовки. Используйте смену пути как дополнение к лимитам, белому списку и второму фактору.
Можно ли обойтись одной двухфакторной аутентификацией?
Для небольшого блога это уже серьёзный шаг, однако полагаться только на неё не стоит. Второй фактор не спасает от уязвимостей в плагинах и не снимает нагрузку от перебора. Лимит запросов и fail2ban остановят сотни ненужных обращений ещё до проверки кода.
Как понять, что админку уже пытаются взломать?
Посмотрите журнал доступа Nginx: частые POST-запросы к /wp-login.php с одних и тех же адресов говорят о переборе. Помогает и плагин журнала действий, который сообщает о неудачных входах. Команда из шага 10 покажет самых активных отправителей за пару секунд.
Что делать, если у меня динамический IP-адрес?
Белый список в таком случае неудобен: адрес меняется, и доступ пропадает. Поднимите VPN с постоянным адресом и разрешите вход только с него. Вариант попроще — вообще не ограничивать панель по адресам, но усилить остальные уровни: лимиты, fail2ban и второй фактор.
Нужен ли WAF, если уже установлен fail2ban?
Это инструменты для разных задач. Fail2ban реагирует на повторяющиеся неудачные попытки, а WAF анализирует содержимое запросов и блокирует эксплуатацию уязвимостей, SQL-инъекции и XSS. Для небольшого сайта хватит плагина с функциями фильтрации, для магазина с оплатой лучше добавить полноценный WAF.
Как часто нужно менять пароли администраторов?
Регулярная смена по календарю приносит меньше пользы, чем длинный уникальный пароль в менеджере паролей. Меняйте его при подозрении на утечку, после ухода сотрудника и после любого инцидента. Вместе со вторым фактором этого достаточно для большинства проектов.