MySQL с настройками по умолчанию небезопасен. Открытый порт 3306, root без пароля, анонимные пользователи — всё это ворота для злоумышленников, которые сканируют интернет именно на такие признаки автоматическими ботами, а не вручную. Выполните эти шаги сразу после установки, до того как база наполнится реальными данными клиентов.
Базовая защита: mysql_secure_installation
sudo mysql_secure_installation
# Ответьте на вопросы:
# Set root password? YES -- придумайте сложный пароль
# Remove anonymous users? YES
# Disallow root login remotely? YES
# Remove test database? YES
# Reload privilege tables? YES
Запретить прослушивание на внешнем интерфейсе
# /etc/mysql/mysql.conf.d/mysqld.cnf
[mysqld]
bind-address = 127.0.0.1 # Только localhost
# или для IPv6:
bind-address = ::1
sudo systemctl restart mysql
После этой настройки к MySQL нельзя подключиться снаружи даже зная пароль — только с localhost или через SSH-туннель. Это закрывает 3306 порт для интернета без единой строчки firewall-правил.
Создавать отдельного пользователя для каждого приложения
-- Создать пользователя только для одной БД
CREATE USER 'myapp'@'localhost' IDENTIFIED BY 'StrongPassword123!';
CREATE DATABASE myapp_db;
GRANT ALL PRIVILEGES ON myapp_db.* TO 'myapp'@'localhost';
FLUSH PRIVILEGES;
-- Никогда не давать GRANT OPTION приложениям:
-- GRANT ALL PRIVILEGES ON *.* TO ... -- ЗАПРЕЩЕНО!
SSH-туннель вместо открытого порта
Общие принципы установки MySQL/MariaDB на VPS разобраны в статье MySQL/MariaDB на VPS, а защиту самого SSH-доступа стоит проверить по статье SSH Hardening.
# Подключиться к MySQL через SSH-туннель (без открытия 3306)
ssh -L 3306:127.0.0.1:3306 user@your-vps-ip -N -f
# Теперь можно подключаться локально:
mysql -u myapp -p -h 127.0.0.1 -P 3306
Разбор типичных ошибок и симптомов взлома
- Host '1.2.3.4' is not allowed to connect to this MySQL server — на самом деле хорошая новость: bind-address или права пользователя работают как задумано и блокируют внешнее подключение.
- Access denied for user 'root'@'localhost' (using password: YES) после множества неудачных попыток в auth.log — признак брутфорса по паролю root; проверьте
/var/log/mysql/error.logна частоту и источник попыток. - Появление незнакомых баз данных или таблиц вида
readme_to_recover— классический след атаки-вымогателя, при которой скрипт удаляет исходные таблицы и оставляет требование выкупа; восстанавливается только из резервной копии, сделанной до инцидента. - Резкий рост general_log без ручного включения — признак того, что кто-то получил доступ и включил логирование для сбора паролей приложений, передаваемых в запросах.
Мониторинг подозрительной активности
# Включить general log (кратковременно для диагностики!)
SET GLOBAL general_log = 'ON';
SET GLOBAL general_log_file = '/var/log/mysql/general.log';
# Найти медленные запросы
SET GLOBAL slow_query_log = 'ON';
SET GLOBAL long_query_time = 2;
# Просмотр текущих подключений
SHOW PROCESSLIST;
Не держите general_log включённым постоянно — он записывает КАЖДЫЙ запрос и быстро заполняет диск, а при высокой нагрузке ощутимо снижает производительность самой СУБД.
Сравнение мер защиты
| Мера защиты | Приоритет | Что закрывает |
|---|---|---|
| mysql_secure_installation | Критический | Пустой root, анонимные пользователи, тестовая БД |
| bind-address = 127.0.0.1 | Критический | Прямой доступ к порту 3306 из интернета |
| Отдельные пользователи для БД | Высокий | Взлом одного приложения не даёт доступ к другим базам |
| SSH-туннель | Высокий | Удалённое администрирование без открытого порта |
| Регулярный бэкап | Средний | Восстановление после вымогательской атаки или ошибки |
Когда этих мер недостаточно
- Приложение и MySQL разнесены на разные серверы в одном ДЦ — тогда
bind-address = 127.0.0.1не подходит, нужен либо VPN между серверами, либо firewall с белым списком конкретных IP на порт 3306. - Несколько разработчиков подключаются к базе с разных рабочих машин — постоянные SSH-туннели для каждого неудобны; здесь уместнее bastion-хост с единой точкой входа и логированием сессий.
- Нужна репликация master-slave между дата-центрами — трафик реплики обязан идти по TLS-соединению между MySQL-инстансами, простого запрета внешнего интерфейса недостаточно.
Как проверить, что защита работает
# Проверяем, что порт 3306 не виден снаружи
nmap -p 3306 your-vps-ip
# Ожидаем: filtered или closed, не open
# Проверяем, что bind-address применился
mysql -u root -p -e "SHOW VARIABLES LIKE 'bind_address';"
# Проверяем список пользователей и их хосты доступа
mysql -u root -p -e "SELECT user, host FROM mysql.user;"
Если сканирование порта снаружи не находит открытый 3306, а список пользователей не содержит анонимных записей и записей с хостом % для критичных аккаунтов — базовая защита настроена верно.