Содержание
Почему 8.4, а не 8.0
MySQL 8.0 — EOL апрель 2026. После этой даты нет патчей безопасности.
MySQL 8.4 LTS — релиз май 2024, поддержка до апреля 2032. Это текущая production-версия для новых проектов.
MySQL 9.x (Innovation) — новые фичи, но не LTS. Для экспериментов, не для продакшна.
Если ты ещё на 8.0 — мигрируй на 8.4. Upgrade совместим: mysql_upgrade не нужен начиная с 8.x, просто обнови пакет.
Первый запуск — mysql_secure_installation
После установки сразу:
mysql_secure_installation
Сделает:
- Установит validate_password плагин
- Запросит сложный root-пароль
- Удалит анонимных пользователей
- Запретит root-логин с удалённых хостов
- Удалит тестовую базу
test
Это минимум. Дальше — подробнее.
Принцип минимальных привилегий
Один пользователь на всё — плохая практика. Создавай пользователей под конкретные задачи:
-- Пользователь для приложения (только чтение/запись своих таблиц)
CREATE USER 'app_user'@'10.0.0.%' IDENTIFIED BY 'StrongPassword123!';
GRANT SELECT, INSERT, UPDATE, DELETE ON myapp.* TO 'app_user'@'10.0.0.%';
-- Пользователь для readonly реплики / отчётов
CREATE USER 'readonly_user'@'10.0.0.%' IDENTIFIED BY 'AnotherPassword!';
GRANT SELECT ON myapp.* TO 'readonly_user'@'10.0.0.%';
-- Пользователь для миграций (только структурные изменения)
CREATE USER 'migration_user'@'localhost' IDENTIFIED BY 'MigrationPass!';
GRANT ALTER, CREATE, DROP, INDEX, REFERENCES ON myapp.* TO 'migration_user'@'localhost';
-- Применить немедленно
FLUSH PRIVILEGES;
Никогда не подключай приложение под root. Если взломают — получат доступ ко всем базам, могут удалить данные, залезть в mysql.user.
Посмотреть что у кого есть:
SHOW GRANTS FOR 'app_user'@'10.0.0.%';
-- Или все пользователи с привилегиями
SELECT user, host, authentication_string FROM mysql.user;
caching_sha2_password — новый дефолт
В MySQL 8.x дефолтный метод аутентификации сменился с mysql_native_password на caching_sha2_password. Это безопаснее — SHA-256 вместо SHA-1.
Проблема: старые клиенты (PHP <8.1, старые JDBC) могут не поддерживать.
-- Проверить метод конкретного пользователя
SELECT user, plugin FROM mysql.user WHERE user = 'app_user';
-- Если клиент не поддерживает caching_sha2_password — включить SSL
-- или переключить на mysql_native_password (менее желательно)
ALTER USER 'app_user'@'10.0.0.%'
IDENTIFIED WITH caching_sha2_password BY 'password'
REQUIRE SSL; -- требуем SSL обязательно
-- Для клиентов которые не умеют SHA2 (временное решение)
ALTER USER 'legacy_user'@'10.0.0.%'
IDENTIFIED WITH mysql_native_password BY 'password';
SSL/TLS — шифрование соединений
MySQL 8.4 включает SSL автоматически при установке (auto-generated сертификаты). Проверить:
SHOW VARIABLES LIKE 'have_ssl'; -- YES
SHOW VARIABLES LIKE 'ssl_ca'; -- путь к CA
SHOW STATUS LIKE 'Ssl_cipher'; -- шифр текущего соединения
Принудить пользователя использовать SSL:
-- При создании
CREATE USER 'secure_user'@'%' IDENTIFIED BY 'password' REQUIRE SSL;
-- Или изменить существующего
ALTER USER 'app_user'@'10.0.0.%' REQUIRE SSL;
-- Более строгий вариант — требовать конкретный X.509 сертификат
ALTER USER 'app_user'@'10.0.0.%' REQUIRE X509;
Проверить из приложения (C# + MySQL Connector):
var connStr = "Server=db;Database=myapp;User=app_user;Password=pass;" +
"SslMode=Required;SslCa=/path/to/ca.pem;";
validate_password — политика паролей
-- Проверить текущие настройки
SHOW VARIABLES LIKE 'validate_password%';
-- Настроить уровень (LOW, MEDIUM, STRONG)
SET GLOBAL validate_password.policy = MEDIUM; -- длина + спецсимволы
SET GLOBAL validate_password.length = 12;
SET GLOBAL validate_password.mixed_case_count = 1;
SET GLOBAL validate_password.number_count = 1;
SET GLOBAL validate_password.special_char_count = 1;
-- Проверить пароль без создания пользователя
SELECT VALIDATE_PASSWORD_STRENGTH('MyPassword123!');
-- Возвращает 0-100: <25 LOW, <50 MEDIUM, <75 STRONG, 100 — всё норм
Защита от SQL-инъекций
Инъекции — это проблема кода, не MySQL. Но MySQL помогает обнаружить:
-- Включить general_log на время разработки, чтобы видеть все запросы
SET GLOBAL general_log = 'ON';
SET GLOBAL general_log_file = '/var/log/mysql/general.log';
-- ВЫКЛЮЧИ в продакшне — это медленно и раскрывает данные!
Как защититься в коде:
// НИКОГДА — конкатенация строк
string query = "SELECT * FROM users WHERE email = '" + email + "'";
// SQL-инъекция: email = "' OR 1=1 --" → вернёт всех пользователей
// ПРАВИЛЬНО — параметризованные запросы (ADO.NET)
using var cmd = connection.CreateCommand();
cmd.CommandText = "SELECT * FROM users WHERE email = @email";
cmd.Parameters.AddWithValue("@email", email);
// ПРАВИЛЬНО — EF Core LINQ (автоматически параметризует)
var user = await context.Users
.FirstOrDefaultAsync(u => u.Email == email);
// ПРАВИЛЬНО — EF Core raw SQL с параметрами
var user = await context.Users
.FromSqlInterpolated($"SELECT * FROM users WHERE email = {email}")
.FirstOrDefaultAsync();
// НЕ используй FromSqlRaw с конкатенацией!
Файервол и сетевая изоляция
Если MySQL живёт в контейнере — про изоляцию сетей и секреты в compose есть отдельная статья.
# /etc/mysql/mysql.conf.d/mysqld.cnf
# Слушать только на конкретном интерфейсе (не 0.0.0.0)
bind-address = 127.0.0.1
# Или для Docker/internal network:
# bind-address = 10.0.0.5
# Отключить LOCAL INFILE (позволяет читать файлы с клиента)
local_infile = 0
# Iptables — разрешить только с appserver
iptables -A INPUT -p tcp --dport 3306 -s 10.0.0.10 -j ACCEPT
iptables -A INPUT -p tcp --dport 3306 -j DROP
В Docker — не пробрасывай 3306 наружу без необходимости:
# docker-compose.yml
services:
db:
image: mysql:8.4
# Не добавляй: ports: - "3306:3306" # если не нужен снаружи
networks:
- internal
app:
networks:
- internal
networks:
internal:
driver: bridge
Аудит и мониторинг
-- Логировать неудачные входы
SET GLOBAL log_error_verbosity = 3;
-- Посмотреть попытки брутфорса
SELECT * FROM performance_schema.host_cache
WHERE sum_connect_errors > 0;
-- Сбросить блокировку хоста (если превысил max_connect_errors)
FLUSH HOSTS;
-- Кто сейчас подключён
SELECT user, host, db, command, time, state
FROM information_schema.processlist
WHERE user != 'system user';
MySQL Enterprise Edition поставляется с MySQL Enterprise Audit. Для open-source — MariaDB Audit Plugin или MySQL Community Audit (сторонний).
Резервные копии с шифрованием
# mysqldump с паролем через --defaults-extra-file (не в командной строке!)
cat > /tmp/mysql_creds.cnf <<EOF
[client]
user=backup_user
password=BackupPass!
EOF
chmod 600 /tmp/mysql_creds.cnf
# Бэкап с шифрованием
mysqldump --defaults-extra-file=/tmp/mysql_creds.cnf \
--single-transaction --routines --triggers \
myapp | gzip | openssl enc -aes-256-cbc -salt \
-pass pass:$(cat /etc/backup.key) \
-out /backups/myapp_$(date +%Y%m%d).sql.gz.enc
rm /tmp/mysql_creds.cnf
Почему не пароль в командной строке: ps aux покажет все аргументы процессов — пароль будет виден всем пользователям системы.
💡 После настройки запусти
mysqlcheck --all-databases -u root -p— проверит целостность таблиц. И включиinnodb_file_per_table=ONесли ещё не включено — проще делать бэкапы и освобождать место. Следующий шаг после безопасности — индексы и оптимизация запросов. Авто-аудит конфигурации умеет скилл /mysql-audit для Claude Code.
Обсуждение
Пока нет комментариев. Будьте первым.