База данных — самый ценный актив большинства веб-приложений. Её компрометация означает утечку пользовательских данных, паролей и коммерческой информации. Правильная конфигурация MySQL/MariaDB критически важна.
Первичная защита: mysql_secure_installation
mysql_secure_installation
# Отвечаем:
# Validate password: Y (включить проверку сложности)
# Remove anonymous users: Y
# Disallow root login remotely: Y
# Remove test database: Y
# Reload privilege tables: Y
Создание пользователей с минимальными правами
mysql -u root -p
-- Создать пользователя только для одной БД
CREATE USER 'webapp'@'localhost' IDENTIFIED BY 'StrongPass123!';
GRANT SELECT, INSERT, UPDATE, DELETE ON myapp.* TO 'webapp'@'localhost';
-- Только для чтения (для аналитики)
CREATE USER 'analytics'@'localhost' IDENTIFIED BY 'ReadOnlyPass!';
GRANT SELECT ON myapp.* TO 'analytics'@'localhost';
FLUSH PRIVILEGES;
SHOW GRANTS FOR 'webapp'@'localhost';
Настройка my.cnf для безопасности
# /etc/mysql/mariadb.conf.d/99-security.cnf
[mysqld]
# Привязать только к localhost (запретить внешние подключения)
bind-address = 127.0.0.1
# Отключить LOCAL INFILE (защита от LFI-атак)
local-infile = 0
# Скрыть версию MariaDB
skip-show-database
# Максимальное количество ошибочных подключений
max_connect_errors = 100
# Логировать медленные запросы
slow_query_log = 1
slow_query_log_file = /var/log/mysql/slow.log
long_query_time = 2
Шифрование соединений (SSL/TLS)
# Проверить поддержку SSL
mysql -u root -p -e "SHOW VARIABLES LIKE '%ssl%';"
# Требовать SSL для пользователя
ALTER USER 'webapp'@'%' REQUIRE SSL;
# В my.cnf включить SSL
ssl-ca = /etc/mysql/ssl/ca-cert.pem
ssl-cert = /etc/mysql/ssl/server-cert.pem
ssl-key = /etc/mysql/ssl/server-key.pem
Автоматические бэкапы с шифрованием
cat > /etc/cron.daily/mysql-backup << 'SCRIPT'
#!/bin/bash
DATE=$(date +%Y%m%d)
mysqldump -u root -p'RootPass' --all-databases --single-transaction | gzip | gpg --batch --passphrase "BackupKey123" -c > /backup/mysql-$DATE.sql.gz.gpg
find /backup/ -name "mysql-*.sql.gz.gpg" -mtime +30 -delete
SCRIPT
chmod +x /etc/cron.daily/mysql-backup
0.0.0.0). Это открывает доступ из интернета. Установите bind-address = 127.0.0.1 если внешний доступ не нужен.
Частые ошибки при настройке безопасности
Большинство ошибок доступа после hardening объясняются сменой метода аутентификации или областью действия пользователя.
ERROR 1045 (28000): Access denied for user 'root'@'localhost' (using password: YES)
После mysql_secure_installation root на многих системах переключается на аутентификацию через unix_socket — вход возможен только локально через sudo mysql, а не через mysql -u root -p с паролем. Проверьте метод аутентификации: SELECT user, plugin FROM mysql.user WHERE user='root';.
ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/var/run/mysqld/mysqld.sock' (2)
Служба не запущена, либо путь к сокету в конфиге приложения не совпадает с реальным. Проверьте: systemctl status mariadb и путь socket в [client]-секции my.cnf.
ERROR 1130 (HY000): Host '10.0.0.5' is not allowed to connect to this MySQL server
Пользователь создан только для 'webapp'@'localhost', а подключение идёт с другого сервера или контейнера. Либо создайте пользователя с нужным хостом или подсетью ('webapp'@'10.0.0.%'), либо подключайтесь через SSH-туннель без открытия порта наружу — это безопаснее, чем разрешать удалённый IP напрямую.
Методы аутентификации: сравнение
| Плагин | Хранение пароля | Совместимость | Рекомендация |
|---|---|---|---|
| unix_socket | Пароль не нужен, проверка по UID ОС | Только локальные подключения | Для root и админ-задач на сервере |
| mysql_native_password | SHA1-хэш | Максимальная, старые драйверы | Только если нужна совместимость |
| caching_sha2_password | SHA-256 | MySQL 8+, современные драйверы | По умолчанию для новых приложений |
MariaDB и MySQL используют разные плагины по умолчанию — на MariaDB 10.4+ это обычно unix_socket для root, на MySQL 8 — caching_sha2_password для новых пользователей. При переносе дампа между СУБД пароли пользователей иногда приходится назначать заново.
Когда такой hardening избыточен или не подходит
Если приложение и база находятся на разных серверах (например, отдельный сервер БД для нескольких веб-серверов), жёсткое bind-address = 127.0.0.1 из примера ломает архитектуру — в этом случае вместо открытия порта 3306 наружу настройте приватную сеть между серверами или SSH-туннель, а не публичный доступ по паролю. Легаси-приложения на старых версиях PHP или Python могут не поддерживать caching_sha2_password — если после обновления появляется ошибка совместимости аутентификации, временно создайте пользователя на mysql_native_password, а не откатывайте всю защиту сервера.
Как проверить результат
Проверяйте не «подключение прошло», а конкретные параметры безопасности:
# Убедиться, что пользователь имеет только нужные права
SHOW GRANTS FOR 'webapp'@'localhost';
# Проверить, что порт не слушает 0.0.0.0
sudo ss -tlnp | grep 3306
# Проверить, что SSL реально включён для соединения
mysql -u root -p -e "SHOW STATUS LIKE 'Ssl_cipher';"
Команда ss -tlnp | grep 3306 должна показать адрес 127.0.0.1:3306, а не 0.0.0.0:3306, если внешний доступ не нужен. Пустое значение Ssl_cipher означает, что соединение прошло без шифрования, даже если сертификаты настроены в конфиге, — клиент тоже должен явно запрашивать SSL.
Частые вопросы
Нужно ли менять порт MySQL по умолчанию ради безопасности?
Смена порта сама по себе не заменяет настоящую защиту — она лишь немного усложняет автоматическое сканирование, но не останавливает целенаправленную атаку. Куда важнее ограничить bind-address локальным интерфейсом и держать доступ закрытым файрволом снаружи, а порт при этом можно оставить стандартным без заметной потери безопасности.
Нужно ли вообще удалять пользователя root, а не просто запрещать ему удалённый доступ?
Нет, удалять системного пользователя root не требуется и обычно невозможно без последствий — многие служебные операции и утилиты рассчитывают на его существование. Правильный подход — запретить ему удалённое подключение и переключить аутентификацию на локальный сокет, оставив полный доступ только при входе прямо на сервере.
Безопасно ли использовать одного пользователя базы данных для нескольких разных приложений?
Нет, общий пользователь для нескольких приложений ломает принцип минимальных привилегий: компрометация одного приложения сразу открывает доступ ко всем базам данных остальных. Правильнее создать отдельного пользователя под каждое приложение с правами только на нужную ему базу, как и показано в примере с разделением прав на запись и только чтение.
Что будет, если забыть создать нового пользователя БД после восстановления дампа на другом сервере?
Дамп базы данных переносит только структуру и содержимое таблиц, а не системных пользователей и их права доступа — их нужно создавать на новом сервере отдельно, вручную или отдельным дампом таблицы привилегий. Без этого шага приложение на новом сервере просто не сможет подключиться с теми же учётными данными, что и раньше.