Почему «поставили и забыли» заканчивается взломом
Меня зовут Евгений Семёнов, я технический директор ITfresh. Мы занимаемся IT-аутсорсингом для московских компаний до пятидесяти рабочих мест, держим свои серверы в дата-центре МТС, и вместе с инфраструктурой клиентов к нам регулярно «в нагрузку» попадают их сайты. В том числе на Joomla — про саму CMS, её установку и миграцию я уже писал в предыдущих статьях серии. Сегодня — про то, о чём обычно вспоминают слишком поздно: что с сайтом на Joomla делать после запуска.
Типовая картина, с которой к нам приходят. Звонок: «Наш сайт рассылает спам, хостер грозит блокировкой». Открываем — Joomla 3.x, не обновлялась четыре года, пиратский шаблон с «подарком» внутри, учётка admin с паролем из телефонного номера директора, в папке /images живёт десяток PHP-шеллов, а в базе — левые учётки суперпользователей. Сайт при этом внешне работал и никого не беспокоил. Ломается сайт не в момент установки — он ломается на втором-третьем году жизни без обслуживания.
Важный момент, который я повторяю каждому клиенту: подавляющее большинство взломов CMS-сайтов происходит не через ядро, а через устаревшие сторонние расширения и шаблоны. Ядро Joomla закрывают быстро — проект живой, security-релизы выходят регулярно. А вот заброшенный плагин слайдера от студии, закрывшейся в 2021 году, не закроет уже никто. Плюс классика: слабые пароли админки и заражённые компьютеры сотрудников, с которых утекают сохранённые пароли.
Ниже — наш внутренний регламент эксплуатации Joomla-сайтов на сопровождении. Он одинаково применим и на shared-хостинге, и на VPS: где команды различаются, я это отмечаю. Забирайте и пользуйтесь — даже частичное внедрение сильно снижает риски.
Регламент обновлений
Обновления — скучная, рутинная и самая важная часть эксплуатации. У нас они разложены на три потока с разными приоритетами.
Security-релизы — в течение 72 часов
Свежий пример из этого лета. 7 июля 2026 года проект выпустил Joomla 6.1.2 и 5.4.7 — сразу двенадцать security-фиксов, в основном ошибки контроля доступа и XSS. За пару месяцев до того, 26 мая, выходили 6.1.1 и 5.4.6, закрывшие десять CVE, включая SQL-инъекции. Это нормальный ритм живого проекта, и на него надо уметь реагировать.
Как выглядела наша «волна» 7–9 июля: в день релиза — уведомление из мониторинга по всем клиентским сайтам, где версия отстала; вечером — обновление тестового стенда и прогон чек-листа (логин, формы, оформление ключевых страниц); на следующий день — обновление боевых сайтов по одному, с бэкапом перед каждым. К исходу третьих суток все сайты на сопровождении были на свежих версиях. Правило 72 часов родилось из практики: публичный эксплойт по мотивам патча часто появляется в течение недели после релиза, и опоздавшие попадают под автоматизированные сканы.
Минорные и функциональные версии — раз в квартал
Релизы вида 6.1 → 6.2 мы не ставим в день выхода. Они попадают в квартальное окно обслуживания: сначала staging-копия сайта, прогон по чек-листу, проверка совместимости расширений — и только потом продакшен. Спешить некуда: функциональные релизы не закрывают дыр, а вот сломать несовместимое расширение могут.
Расширения — опаснее ядра
Парадокс, к которому я пришёл за годы: обновление ядра Joomla почти никогда не ломает сайт, обновление стороннего расширения — регулярно. Поэтому для расширений у нас отдельные правила:
- перед обновлением читаем changelog: security-фикс — ставим быстро, «переписали архитектуру» — только через staging;
- раз в квартал сверяем список установленных расширений с официальной базой уязвимостей расширений Joomla (Vulnerable Extensions List) — если расширение там и не чинится автором, оно кандидат на замену;
- расширение, которое автор не обновлял два года и которое молчит на новых версиях PHP, планово заменяем, не дожидаясь проблем.
Автоматизация через CLI
Начиная с четвёртой версии у Joomla есть полноценная консоль, и это сильно упрощает жизнь на VPS. Обновление ядра без клика по админке:
php /var/www/site/cli/joomla.php core:check-updates
php /var/www/site/cli/joomla.php core:update
А вот наш дежурный крон-скрипт, который раз в сутки сверяет версию каждого подопечного сайта и шлёт алерт в Telegram, если вышло обновление:
#!/bin/bash
SITE=/var/www/site
CUR=$(php $SITE/cli/joomla.php core:check-updates 2>&1)
echo "$CUR" | grep -q "up to date" && exit 0
curl -s -X POST "https://api.telegram.org/bot$TOKEN/sendMessage" \
-d chat_id=$CHAT -d text="Joomla update available: $SITE — $CUR"
На shared-хостинге, где SSH не дают, то же самое делает внешний мониторинг: версия Joomla видна в манифесте administrator/manifests/files/joomla.xml, и наблюдатель сравнивает её с последним релизом.
Бэкапы: схема 3-2-1 для сайта на CMS
Формула 3-2-1 в применении к сайту: три копии данных, минимум два независимых инструмента, минимум одна копия за пределами хостинга. Звучит избыточно ровно до первого серьёзного инцидента.
Копия №1 — Akeeba Backup. Стандарт де-факто в мире Joomla, бесплатной редакции Core хватает для наших задач. Актуальная ветка 10.3 (вышла в конце июля 2026) поддерживает Joomla с 4.4 по 6.1 и PHP вплоть до 8.5, а с этой версии умеет бэкапить и PostgreSQL-сайты. Ежедневный полный JPA-архив (файлы + дамп базы) запускается по крону штатной консольной командой:
10 2 * * * /usr/bin/php /var/www/site/cli/joomla.php akeeba:backup:take
Копия №2 — независимый скрипт. Принцип «не доверяй одному инструменту»: если баг Akeeba однажды сделает архивы невосстановимыми, узнать об этом в день аварии — плохой сценарий. Поэтому параллельно, в другое время суток, работает примитивный и оттого надёжный тандем:
mysqldump --single-transaction -u backup -p"$PW" sitedb | gzip \
> /backup/db-$(date +%F).sql.gz
tar -czf /backup/files-$(date +%F).tar.gz -C /var/www site \
--exclude=site/cache --exclude=site/tmp
Копия №3 — вне хостинга. Обе локальные копии ночью уезжают на внешнее хранилище. У нас это собственный FTP-сервер в дата-центре — физически другая площадка; у клиента это может быть любое S3-совместимое облако или диск в офисе. Смысл один: смерть хостинга (пожар, блокировка аккаунта, шифровальщик) не должна убить бэкапы вместе с сайтом.
Ротация у нас — 7 дневных, 4 недельных, 6 месячных копий. Месячные важны: заражение сайта нередко обнаруживают через недели, и «вчерашний» бэкап к этому моменту уже содержит шеллы.
Hardening: закручиваем гайки
Дальше — разовая настройка, которая делается при взятии сайта на обслуживание и потом только поддерживается.
Админка
- Никакого пользователя admin. Логин суперпользователя — неугадываемый; учётки уволенных редакторов блокируются в день увольнения.
- Многофакторная аутентификация из ядра. Joomla штатно умеет TOTP (коды из приложения) и WebAuthn (аппаратные ключи) — включаем всем, у кого права выше «Автора». Плагинов не нужно.
- Прячем /administrator. На VPS — ограничение по IP офиса прямо в nginx; для сотрудников на удалёнке — доступ через VPN. Где белого IP нет, работает старый приём с секретным параметром входа, который дают плагины класса «admin tools»: без
?tokenадминка отдаёт 404.
Файловая система
Права — классические 644 на файлы и 755 на каталоги, владелец — пользователь сайта, не root. И обязательный пункт, который срезает большинство последствий взлома: запрет исполнения PHP в каталогах загрузок. Залитый в /images шелл превращается из катастрофы в мусорный файл.
Для nginx:
location ~* ^/(images|cache|tmp|media)/.*\.php$ { deny all; }
Для Apache — .htaccess в каждом из этих каталогов:
<FilesMatch "\.ph(p[0-9]?|tml)$">
Require all denied
</FilesMatch>
configuration.php
Три строки, которые мы проверяем на каждом принимаемом сайте: $force_ssl = 2; (HTTPS и для витрины, и для админки), $error_reporting = 'none'; на проде (пути и трейсы на экране — подарок атакующему), и $log_path / $tmp_path, вынесенные за пределы веб-корня, чтобы логи не читались браузером.
Меньше расширений — меньше дыр
Каждое установленное расширение — это чужой код с правами вашего сайта. На типовом «принятом в наследство» сайте мы находим 30–40 расширений, из которых реально используются 10–15. Остальные не «отключаем на всякий случай», а удаляем: отключённое расширение исчезает из меню, но его файлы остаются на диске и продолжают быть поверхностью атаки.
Защита от брутфорса и ботов без внешних сервисов
Отдельная боль российских сайтов — завязка защитных механизмов на Google: reCAPTCHA то работает, то нет, в зависимости от провайдера и настроения блокировок. Поэтому наш стек защиты от перебора паролей и спам-ботов собран целиком из того, что не зависит от внешних сервисов.
Встроенная капча Joomla. В актуальной ветке 6.1 у Joomla появилась своя proof-of-work капча: браузер посетителя решает вычислительную задачку, человек не видит вообще ничего, а массовая рассылка форм становится для бота дорогой. Включаем её на форме входа, регистрации и контактных формах — три клика в настройках, ноль внешних зависимостей. После многих лет костылей с «вопросом про столицу России» это праздник.
fail2ban на VPS. Joomla пишет неудачные входы в свой лог, и на этом строится фильтр:
# /etc/fail2ban/filter.d/joomla-auth.conf
[Definition]
failregex = ^.*INFO <HOST>.*Username and password do not match.*$
# /etc/fail2ban/jail.d/joomla.conf
[joomla-auth]
enabled = true
port = http,https
logpath = /var/www/site/logs/error.php
maxretry = 5
bantime = 3600
Пять неверных паролей — бан IP на час. Дёшево и сердито.
Лимиты на уровне nginx. Даже до капчи и fail2ban долетает не всё, если админка прикрыта rate-limit'ом:
limit_req_zone $binary_remote_addr zone=admlogin:10m rate=30r/m;
location /administrator/ {
limit_req zone=admlogin burst=10 nodelay;
...
}
По нашей статистике мониторинга, после включения этой связки — капча плюс fail2ban плюс rate-limit — количество регистрируемых попыток брутфорса на клиентском сайте падает с тысяч в сутки до единиц: боты-сканеры быстро понимают, что здесь ловить нечего, и уходят к менее защищённым соседям.
Мониторинг и реагирование
Регламент без контроля мёртв, поэтому по каждому сайту на сопровождении круглосуточно смотрят автоматические проверки:
- доступность и время ответа — с внешней точки, раз в минуту;
- срок действия TLS-сертификата — алерт за две недели до конца;
- версия ядра — отстала от актуальной → задача инженеру;
- целостность файлов — ночное сравнение хэшей файлов ядра и шаблонов с эталоном, снятым после последнего легального обновления;
- новые PHP-файлы там, где их не должно быть — появление
*.phpв/imagesили/tmp— это алерт с максимальным приоритетом, в любое время суток.
Все события падают в Telegram дежурному. Проверка хэшей — это буквально find + md5sum по списку каталогов и diff с эталонным файлом; никакой магии, но именно она ловит взлом в первые часы, а не через месяц по жалобе хостера.
Если взлом всё-таки случился, действуем по отработанному плану:
- Изоляция. Сайт закрывается заглушкой, чтобы остановить рассылку спама/заразы и не копить ущерб.
- Анализ. По логам веб-сервера и датам изменения файлов восстанавливаем картину: когда вошли, через что, что успели изменить. Это важно, чтобы выбрать точку восстановления до заражения.
- Восстановление из чистого бэкапа — вот где выстреливает ротация с месячными копиями.
- Закрытие дыры. Восстановить и не закрыть вектор — значит повторить всё через неделю.
- Смена всех паролей: админка, база, FTP/SSH, панель хостинга.
- Отчёт клиенту — что случилось, что сделали, что меняем в регламенте.
Производительность под нагрузкой в долгую
Сайт, который никто не обслуживает, со временем не только дырявеет, но и тяжелеет. Несколько пунктов из регламента, которые касаются именно «здоровья» в долгую.
Кэш и сессии. Раз в месяц чистим кэш через админку или cli/joomla.php cache:clean и приглядываем за таблицей #__session — при сбоях планировщика она разрастается до сотен мегабайт и заметно тормозит вход на сайт.
Рост базы. У возрастных Joomla-сайтов есть характерная болячка — раздутая таблица #__assets и хвосты от удалённых расширений. Плюс история версий материалов: по умолчанию Joomla хранит правки контента, и за годы это гигабайты. Ограничиваем историю десятью версиями на материал, остальное чистим.
Версия PHP. Ядро шестой ветки требует минимум PHP 8.1, но держать сайт мы стараемся на 8.3–8.4: каждая мажорная версия PHP даёт ощутимый прирост скорости бесплатно — надо только проверить расширения на staging перед переключением. На shared-хостингах версия меняется кнопкой в панели, это самая дешёвая оптимизация из существующих.
Когда пора с shared на VPS. Признаки из практики: время ответа сервера стабильно выше 600–800 мс при включённом кэше; хостер пишет письма про превышение лимитов CPU; в пиковые часы сайт отдаёт 503; нужен fail2ban, свои крон-задачи чаще раза в 5 минут или нестандартные модули PHP. Любые два пункта из списка — и переезд на VPS за 800–1500 руб/мес окупается первой же неделей сэкономленных нервов.
Сколько это стоит и как выглядит в договоре сопровождения
Всё описанное — не heroic effort, а конвейер. В типовой пакет обслуживания сайта у нас входит: мониторинг 24/7, security-обновления по правилу 72 часов, квартальные окна обновлений, бэкапы по схеме 3-2-1 с ежеквартальной проверкой восстановления и несколько часов правок контента/вёрстки в месяц. Для компании это единицы тысяч рублей в месяц — меньше, чем разовое лечение взлома, и несравнимо меньше, чем простой сайта в горячий сезон.
Почему мы отдаём сопровождение сайта в связке с обслуживанием всей IT-инфраструктуры: сайт не живёт в вакууме. Пароль от админки лежит в браузере бухгалтера, почта домена настроена на том же DNS, форма с сайта шлёт заявки через корпоративный SMTP. Когда за компьютеры, почту и сайт отвечает одна команда, не бывает футбола «это не мы, это ваш веб-мастер».
Напоследок — чек-лист из 15 пунктов. Пройдитесь по своему сайту, честно ставя плюсы и минусы:
| # | Проверка | Ок? |
|---|---|---|
| 1 | Версия Joomla — актуальная в своей ветке (6.1.2 / 5.4.7 на момент статьи) | ☐ |
| 2 | Все расширения обновлены, заброшенных нет | ☐ |
| 3 | Неиспользуемые расширения удалены, а не отключены | ☐ |
| 4 | Суперпользователь — не «admin», пароль из менеджера паролей | ☐ |
| 5 | MFA включена для всех, кто может править сайт | ☐ |
| 6 | /administrator ограничен (IP, VPN или секретный токен) | ☐ |
| 7 | Исполнение PHP в /images, /cache, /tmp запрещено | ☐ |
| 8 | force_ssl=2, error_reporting=none на проде | ☐ |
| 9 | Ежедневный бэкап двумя независимыми инструментами | ☐ |
| 10 | Копия бэкапа хранится вне хостинга | ☐ |
| 11 | Восстановление проверялось за последние 3 месяца | ☐ |
| 12 | Капча на логине/формах не зависит от Google | ☐ |
| 13 | Брутфорс ограничен (fail2ban / rate-limit / плагин) | ☐ |
| 14 | Мониторинг доступности и сертификата с алертами | ☐ |
| 15 | Известно, кто и в какой срок реагирует на взлом | ☐ |
Три и больше минусов — сайт живёт до первого серьёзного скана. Пять и больше — скорее всего, вопрос не «если», а «когда». Хорошая новость: весь регламент из этой статьи внедряется на один сайт за день-два работы, и дальше требует лишь дисциплины.
Оставить комментарий