Взломали один сайт из десяти на сервере: как не потерять остальные
Один из десяти сайтов на сервере вдруг начал раздавать редиректы на подозрительные домены или в логах nginx появились POST-запросы к несуществующим файлам. Первая мысль — почистить заражённый сайт и жить дальше. Вторая, более тревожная — а что с остальными девятью? Если все сайты годами жили под одним пользователем www-data с общими правами на файлы, ответ почти всегда неприятный: злоумышленник мог дотянуться и до них. Разберём, как быстро понять реальный масштаб проблемы и что сделать, чтобы один взлом не превратился в девять.
Содержание
Почему один взлом редко остаётся одним
На классическом shared-хостинге или на VPS, где несколько сайтов подняты «по-быстрому» без разграничения, у всех сайтов часто один и тот же владелец файлов — обычно www-data, nginx или apache — и один и тот же пользователь, от имени которого работает PHP-FPM или mod_php. Это значит, что скрипт, который выполняется в контексте одного сайта, физически способен читать, а нередко и писать в директории всех остальных, потому что для операционной системы это один и тот же UID.
Веб-сервер не делает различий между «своими» и «чужими» файлами внутри одного пользователя — он просто проверяет права unix. Если /var/www/site-a и /var/www/site-b принадлежат одному www-data:www-data с правами 755/644, PHP-процесс сайта A может открыть ../site-b/wp-config.php и прочитать пароль от чужой базы данных, не встретив никаких препятствий на уровне ОС. Это не гипотетика — именно так массово заражают целые серверы: находят самый слабый сайт (устаревший плагин, дырявая тема), а дальше горизонтально расползаются по остальным через общий доступ на файловую систему.
Отдельно стоит учитывать типовые сценарии заражения: веб-шелл, залитый через уязвимую форму загрузки, добавленная в крон задача, подмена файлов ядра CMS, инъекция в .htaccess для редиректов. У каждого свой набор следов, но объединяет их одно — как только код исполнился под общим пользователем, дальнейшее распространение ограничено уже не логикой сайта, а правами файловой системы. Поэтому первый и главный вопрос после обнаружения взлома — не «что сломали на сайте A», а «мог ли процесс сайта A дотянуться до B, C и остальных».
Как быстро оценить масштаб: ищем следы бокового перемещения
Прежде чем чистить заражённый сайт, нужно понять, ушёл ли злоумышленник дальше него. Начните с проверки, кто на самом деле владеет файлами всех сайтов на сервере:
for d in /var/www/*/; do
echo "== $d =="
stat -c '%U:%G' "$d"
done
Если у всех директорий один и тот же владелец — это уже сигнал тревоги независимо от того, нашли вы следы взлома в других сайтах или нет: техническая возможность добраться до соседей была изначально.
Дальше — ищем файлы, изменённые примерно в то же время, когда предположительно произошёл взлом заражённого сайта. Если вы знаете время компрометации (например, по времени появления веб-шелла), проверьте все директории сайтов на изменения в этом окне:
find /var/www -type f -newermt "2026-08-20 00:00" -newermt "2026-08-27 23:59" \
\( -name "*.php" -o -name "*.js" -o -name "*.htaccess" \) -printf '%T@ %p\n' | sort -n
Отдельно проверьте, не появились ли за это время файлы с подозрительными именами или расширениями — веб-шеллы часто маскируются под системные файлы CMS:
find /var/www -type f -name "*.php" -mtime -14 -exec ls -la {} \;
grep -rl "eval(base64_decode\|gzinflate\|str_rot13\|assert(\$_" /var/www --include="*.php" 2>/dev/null
Проверьте логи веб-сервера на предмет запросов, которые обращались не к домену пострадавшего сайта, а к путям других сайтов — это прямой признак того, что взломанный сайт использовался как плацдарм:
grep -i "site-b\|site-c" /var/log/nginx/site-a.access.log
Если PHP-FPM у сайтов общий пул (то есть один и тот же системный пользователь), посмотрите на процессы, которые были активны в момент атаки, и на что они успели открыть доступ:
ps aux | grep php-fpm
lsof -u www-data | grep -v "site-a"
Последняя команда особенно показательна: если под пользователем взломанного сайта открыты файловые дескрипторы в директориях других сайтов — это уже не теория, а факт бокового перемещения, который нужно расследовать по каждому затронутому сайту отдельно, как отдельный инцидент. Общий план действий при обнаруженном взломе — включая работу с логами, временной шкалой и восстановлением — подробно разобран в статье «Взломали сервер: пошаговый план»; в ситуации с несколькими сайтами этот план нужно прогонять по каждому подозрительному сайту, а не только по исходно заражённому.
Проверьте также crontab всех системных пользователей на сервере, а не только пострадавшего сайта — закрепление через cron с общим пользователем позволяет злоумышленнику вернуться даже после чистки файлов одного сайта:
for u in $(cut -f1 -d: /etc/passwd); do
echo "== $u =="; crontab -u "$u" -l 2>/dev/null
done
cat /etc/cron.d/* 2>/dev/null
Если находите незнакомую задачу у пользователя, которого никто не заводил вручную — это отдельный и очень тревожный сигнал: значит, атакующий уже закрепился на уровне системы, а не только внутри одного сайта.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверИзоляция сайтов друг от друга: с чего реально начинается защита
Главный вывод из предыдущего раздела обычно один: если сайты не изолированы, границу между «взломан один» и «взломаны все» проводит не архитектура, а везение и внимательность атакующего. Изоляция строится на трёх уровнях, и работать они должны вместе — отдельно каждый решает проблему лишь частично.
Отдельный системный пользователь на каждый сайт. Вместо общего www-data для всех доменов заводите по unix-пользователю на сайт:
useradd -r -s /usr/sbin/nologin -d /var/www/site-a site-a
useradd -r -s /usr/sbin/nologin -d /var/www/site-b site-b
chown -R site-a:site-a /var/www/site-a
chown -R site-b:site-b /var/www/site-b
Флаг -s /usr/sbin/nologin важен: этому пользователю не нужен интерактивный шелл, он существует только как владелец файлов и процессов PHP-FPM. Такое разделение подробно разбирается в общем материале про управление пользователями и группами в Linux — принципы оттуда напрямую применимы к изоляции сайтов на общем сервере.
Отдельный пул PHP-FPM на каждый сайт с собственным пользователем. Общий пул со сквозным www-data — самая частая причина, по которой один взлом становится массовым. Правильная конфигурация — свой файл пула на сайт:
# /etc/php/8.3/fpm/pool.d/site-a.conf
[site-a]
user = site-a
group = site-a
listen = /run/php/site-a.sock
listen.owner = site-a
listen.group = www-data
pm = dynamic
pm.max_children = 10
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 4
php_admin_value[open_basedir] = /var/www/site-a:/tmp
php_admin_value[disable_functions] = exec,passthru,shell_exec,system,proc_open,popen
Обратите внимание на open_basedir — эта директива дополнительно ограничивает PHP на уровне интерпретатора: даже если атакующий получит выполнение кода внутри сайта A, попытка открыть /var/www/site-b/wp-config.php вернёт ошибку доступа, потому что путь физически вне разрешённой зоны, независимо от unix-прав. Это не замена системной изоляции, а дополнительный барьер — оба уровня стоит держать вместе. Про то, сколько сайтов вообще стоит держать в одном пуле PHP-FPM и когда пул начинает захлёбываться, — отдельный разбор в статье «Сколько сайтов на одном PHP-FPM»: изоляция и производительность здесь связаны напрямую, потому что раздельные пулы заодно защищают сайты друг от друга по нагрузке.
В nginx для каждого сайта прописывается свой сокет:
# /etc/nginx/sites-available/site-a
server {
server_name site-a.example;
root /var/www/site-a;
location ~ \.php$ {
fastcgi_pass unix:/run/php/site-a.sock;
include fastcgi_params;
}
}
Ограничение прав на файловую систему по минимуму необходимого. Даже при раздельных пользователях стоит убрать лишние права:
find /var/www/site-a -type f -exec chmod 640 {} \;
find /var/www/site-a -type d -exec chmod 750 {} \;
chmod 600 /var/www/site-a/wp-config.php
Директории заливки (uploads, wp-content/uploads и подобные) — отдельная зона риска: туда часто заливают веб-шеллы под видом изображений. Запретите там выполнение PHP на уровне nginx:
location ~* /uploads/.*\.php$ {
deny all;
return 403;
}
Базы данных: не забывайте про них при изоляции
Изоляция файловой системы решает только половину задачи, если MySQL или PostgreSQL при этом настроены так, что учётная запись одного сайта видит базы данных других. Проверьте права каждого пользователя БД:
SHOW GRANTS FOR 'site_a_user'@'localhost';
Если видите GRANT ALL PRIVILEGES ON *.* — это означает, что взломанный сайт с таким пользователем в конфиге потенциально дал атакующему доступ вообще ко всем базам на сервере, включая чужие. Правильная схема — по учётной записи на базу с правами строго на неё:
CREATE USER 'site_a_user'@'localhost' IDENTIFIED BY 'сложный-пароль';
GRANT SELECT, INSERT, UPDATE, DELETE ON site_a_db.* TO 'site_a_user'@'localhost';
FLUSH PRIVILEGES;
Если при разборе инцидента вы обнаружили общего пользователя MySQL для нескольких сайтов — считайте скомпрометированными все базы, к которым у него был доступ, даже если явных следов вмешательства в них пока нет: злоумышленник мог просто не успеть или не счёл нужным. Меняйте пароли и пересоздавайте учётные записи с ограниченными правами для каждой базы отдельно, а не только для той, что была явно атакована.
Срочные меры, если взломан один сайт из многих
Когда факт взлома подтверждён и понятно, что сервер держит ещё несколько сайтов, порядок действий отличается от чистки одиночного сайта — здесь приоритет на сдерживании, а не на немедленной очистке:
- Изолируйте заражённый сайт от остальных прямо сейчас, даже если постоянная изоляция ещё не выстроена. Временно смените владельца файлов взломанного сайта на отдельного пользователя без доступа к остальным директориям:
useradd -r -s /usr/sbin/nologin quarantine-site-a
chown -R quarantine-site-a:quarantine-site-a /var/www/site-a
Это не решит проблему полностью, но немедленно остановит дальнейшее горизонтальное распространение через общий пул.
- Остановите PHP-FPM пул скомпрометированного сайта, если он общий с другими, вместо того чтобы сразу пытаться найти и удалить конкретный веб-шелл — на это нужно время, а пул с общим пользователем всё это время остаётся угрозой:
systemctl stop php8.3-fpm
# переключить на индивидуальные пулы, затем поднять остальные сайты по одному
- Проверьте все остальные сайты на признаки компрометации, а не полагайтесь на то, что раз внешних жалоб нет — значит, всё чисто. Пройдитесь по каждому чек-листом: изменённые файлы за последние недели, посторонние записи в crontab, неизвестные административные учётки в CMS, исходящий трафик на незнакомые IP.
- Смените все пароли и ключи, до которых теоретически мог дотянуться взломанный сайт — это база данных самого сайта, но также и SSH-ключи, если они лежали в домашней директории общего пользователя, API-токены в конфигах соседних сайтов, если они были доступны на чтение.
- Проверьте исходящие соединения и открытые порты на сервере целиком, а не только на уровне одного сайта — если атакующий закрепился глубже файлов сайта, он мог поднять reverse shell или добавить SSH-ключ в
authorized_keysсистемного пользователя:
ss -tulpn
last -a | head -30
cat /root/.ssh/authorized_keys /home/*/.ssh/authorized_keys 2>/dev/null
- Только после сдерживания переходите к полной чистке и восстановлению каждого затронутого сайта — из бэкапа, сделанного заведомо до момента компрометации, с обязательной сменой всех учётных данных. Общий алгоритм действий при инциденте, включая коммуникацию с клиентами и сохранение улик для разбора причин, описан в материале «Incident response plan: что делать при взломе» — используйте его как чек-лист для каждого пострадавшего сайта отдельно.
Важный нюанс: если у вас нет технической возможности быстро внедрить полную изоляцию (раздельные пользователи, пулы, права), временная мера — хотя бы развести сайты по разным серверам или VPS до тех пор, пока не наведёте порядок. Это дороже, чем донастройка одного сервера, но радикально снижает риск повторного каскадного заражения, пока изоляция не выстроена как следует.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверНужны сами нейросети для контента?
Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.
Частые вопросы
Как понять, что взлом одного сайта затронул остальные, если явных признаков (дефейс, редиректы) на них нет?
Отсутствие видимых симптомов не означает отсутствие компрометации — веб-шелл или бэкдор может месяцами не проявлять себя внешне. Проверяйте не по симптомам, а по фактам: изменённые файлы за период возможного взлома, права доступа общего пользователя, записи в логах об обращениях взломанного сайта к директориям других.
Обязательно ли делать раздельные пулы PHP-FPM для каждого сайта, или достаточно chown на файлы?
Раздельные права на файлы снижают риск, но если PHP всё ещё выполняется от одного и того же системного пользователя во всех пулах, процесс одного сайта может открыть файлы другого, если на них не выставлены индивидуальные права владельца. Полная изоляция требует обоих уровней вместе: отдельный пользователь плюс отдельный пул с open_basedir.
Сколько времени в среднем занимает перевод десятка сайтов с общего пользователя на изолированную схему?
Точных цифр без деталей вашей конфигурации не дам — это зависит от количества сайтов, их технологического стека и наличия автоматизации (Ansible, скрипты миграции). Ориентировочно на один сайт с ручной миграцией уходит от 30 минут до пары часов, включая тестирование, что он продолжает работать после смены пользователя и пула.
Если сервер арендован у хостера, а не администрируется своими силами — кто должен обеспечивать изоляцию сайтов?
На выделенных серверах и VPS с полным root-доступом ответственность за изоляцию сайтов друг от друга лежит на том, кто настраивает веб-сервер — обычно на владельце сервера или его администраторе. Хостер отвечает за инфраструктуру (сеть, железо, гипервизор), но не за то, как вы разграничиваете сайты внутри своей операционной системы.
Стоит ли вообще держать несколько сайтов на одном сервере, если один взлом создаёт риск для всех?
Это нормальная и экономически оправданная практика, если изоляция выстроена правильно — отдельные пользователи, пулы и права на файлы сводят риск бокового перемещения к минимуму, сопоставимому с раздельными серверами. Проблема не в самом соседстве сайтов, а в его отсутствии на уровне системных настроек.
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →