MySQL 加固核心目标:限制未授权访问、最小化权限暴露、加密敏感数据、审计操作行为,以下方案覆盖从基础配置到高级防护的全流程,适配 MySQL 5.7+ / 8.0+(标注版本差异处需重点注意)。
一、基础环境加固(系统 + 安装层面)
1. 升级到稳定安全版本
旧版本(如 5.6 及以下)存在大量未修复漏洞(如权限绕过、SQL 注入),优先升级至:
- MySQL 5.7.30+(LTS 长期支持版)
- MySQL 8.0.20+(推荐,安全性更强,默认认证插件更安全)
升级检查:
sql
SELECT VERSION(); -- 查看当前版本
2. 以非 root 用户运行 MySQL
默认 MySQL 可能以 root 用户启动,若被入侵可能导致服务器提权,需创建专用低权限用户:
bash
# 1. 创建 mysql 系统用户(若未创建)
useradd -r -s /sbin/nologin mysql
# 2. 修改数据目录/配置文件权限(Linux 示例)
chown -R mysql:mysql /var/lib/mysql # 数据目录
chown -R mysql:mysql /etc/my.cnf # 配置文件
chmod 700 /var/lib/mysql # 仅属主可读写
chmod 600 /etc/my.cnf # 仅属主可修改
# 3. 重启 MySQL 并验证运行用户
systemctl restart mysqld
ps -ef | grep mysqld | grep -v grep # 确认进程属主为 mysql
3. 清理无用组件与文件
- 卸载未使用的存储引擎(如 CSV、BLACKHOLE,仅保留 InnoDB);
- 删除安装残留的测试数据库(
test库)和示例表; - 移除无用的脚本(如
mysql_install_db旧脚本,MySQL 8.0 已废弃)。
删除 test 库:
sql
DROP DATABASE IF EXISTS test;
二、访问控制加固(核心:限制 "谁能连、从哪连")
1. 初始化安全配置(必做)
MySQL 提供内置工具 mysql_secure_installation,一键完成基础加固(需登录 MySQL 后执行):
bash
mysql_secure_installation
按提示完成以下操作:
- 设置 / 修改 root 密码(若未设置);
- 移除匿名用户(
anonymous用户,允许空密码登录); - 禁止 root 用户远程登录(默认仅允许 localhost 访问);
- 删除 test 数据库及匿名用户对所有数据库的访问权限;
- 刷新权限表(
FLUSH PRIVILEGES)。
2. 清理冗余 / 危险用户
(1)查看所有用户及登录主机
sql
-- MySQL 5.7+
SELECT user, host, authentication_string FROM mysql.user;
-- MySQL 8.0+(默认认证插件为 caching_sha2_password)
SELECT user, host, plugin FROM mysql.user;
(2)删除无用用户
- 匿名用户(
user=''或host='%'的空用户); - 长期未使用的账号;
- 权限过大的普通用户。
sql
-- 删除匿名用户(示例)
DROP USER IF EXISTS ''@'localhost';
DROP USER IF EXISTS ''@'%';
-- 删除指定用户(如 test@'%')
DROP USER IF EXISTS 'test'@'%';
FLUSH PRIVILEGES; -- 刷新权限
3. 限制用户登录主机(最小化访问范围)
避免用户以 host='%'(允许所有主机登录),仅授权信任的 IP / 网段:
sql
-- 示例1:仅允许本地登录(推荐 root 用户)
CREATE USER IF NOT EXISTS 'root'@'localhost' IDENTIFIED BY 'StrongP@ss123';
-- 示例2:允许指定 IP 登录(如办公网 192.168.1.0/24 网段)
CREATE USER IF NOT EXISTS 'appuser'@'192.168.1.%' IDENTIFIED BY 'AppP@ss456';
-- 错误示例(禁止):允许所有主机登录
CREATE USER 'baduser'@'%' IDENTIFIED BY 'weakpass'; -- 风险极高
4. 最小权限原则分配权限
(1)拒绝给普通用户高危权限
以下权限仅授予 root 或管理员,禁止给应用用户:
SUPER:修改全局参数、终止线程(可能用于提权);FILE:读写服务器文件(可能泄露敏感数据或写入恶意文件);SHUTDOWN:关闭 MySQL 服务;CREATE USER:创建 / 删除用户(权限滥用风险);ALTER/DROP:修改 / 删除表(误操作或恶意破坏)。
(2)应用用户权限示例(仅授予必要权限)
sql
-- 给应用用户仅授予指定库的查询/插入/更新权限
GRANT SELECT, INSERT, UPDATE ON appdb.* TO 'appuser'@'192.168.1.%';
-- 给只读用户仅授予查询权限
GRANT SELECT ON appdb.* TO 'readuser'@'192.168.1.%';
FLUSH PRIVILEGES;
(3)定期回收未使用权限
sql
-- 查看用户权限
SHOW GRANTS FOR 'appuser'@'192.168.1.%';
-- 回收权限(如回收 UPDATE 权限)
REVOKE UPDATE ON appdb.* FROM 'appuser'@'192.168.1.%';
三、密码安全加固(防止暴力破解)
1. 设置强密码策略
(1)MySQL 5.7+ 配置(通过 validate_password 插件)
sql
-- 启用密码验证插件(默认已启用,若未启用则执行)
INSTALL PLUGIN validate_password SONAME 'validate_password.so';
-- 查看当前密码策略
SHOW VARIABLES LIKE 'validate_password%';
-- 设置强密码规则(修改 my.cnf 永久生效)
[mysqld]
validate_password_length = 12 # 密码最小长度 12 位
validate_password_policy = STRONG # 强策略(需包含大小写、数字、特殊字符)
validate_password_special_char_count = 1 # 至少 1 个特殊字符
validate_password_mixed_case_count = 1 # 至少 1 个大小写字母
(2)MySQL 8.0+ 配置(默认启用 validate_password,策略更严格)
sql
-- 8.0 新增参数,禁止密码与用户名/主机名相同
SET GLOBAL validate_password_check_user_name = ON;
-- 永久生效(my.cnf)
[mysqld]
validate_password_length = 12
validate_password_policy = STRONG
validate_password_check_user_name = ON
2. 定期更换密码 + 密码过期策略
sql
-- 设置用户密码过期时间(90 天,MySQL 5.7+ 支持)
ALTER USER 'appuser'@'192.168.1.%' PASSWORD EXPIRE INTERVAL 90 DAY;
-- 强制用户下次登录修改密码
ALTER USER 'appuser'@'192.168.1.%' PASSWORD EXPIRE FORCE;
-- 禁止重复使用最近 5 次密码(MySQL 8.0+ 支持)
SET GLOBAL password_history = 5;
3. 禁用明文存储密码
- MySQL 5.7+ 默认使用
mysql_native_password插件(哈希存储,仍有破解风险); - MySQL 8.0+ 默认使用
caching_sha2_password插件(更安全,支持加盐哈希),建议所有用户切换至此插件:
sql
-- 修改用户认证插件(MySQL 8.0+)
ALTER USER 'appuser'@'192.168.1.%' IDENTIFIED WITH caching_sha2_password BY 'NewStrongP@ss789';
-- 全局默认使用 caching_sha2_password(my.cnf)
[mysqld]
default_authentication_plugin = caching_sha2_password
四、网络安全加固(防止网络层面攻击)
1. 绑定内网 IP,禁止公网监听
默认 MySQL 监听 0.0.0.0(所有网卡),易被公网扫描,修改 my.cnf 绑定内网 IP:
[mysqld]
bind-address = 192.168.1.100 # 仅监听内网 IP
# 若无需远程访问,直接绑定 localhost
# bind-address = 127.0.0.1
重启 MySQL 后验证:
bash
netstat -tuln | grep 3306 # 确认仅监听指定 IP:3306
2. 防火墙限制 3306 端口访问
仅允许信任的 IP / 网段访问 MySQL 端口(3306),禁止公网开放。
(1)Linux 防火墙(firewalld)
bash
# 允许 192.168.1.0/24 网段访问 3306
firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="192.168.1.0/24" port protocol="tcp" port="3306" accept'
# 禁止其他所有 IP 访问 3306
firewall-cmd --permanent --remove-port=3306/tcp
# 重新加载防火墙规则
firewall-cmd --reload
# 验证规则
firewall-cmd --list-rich-rules
(2)Windows 防火墙
在 "高级设置" 中创建入站规则:仅允许指定 IP 访问 3306 端口,拒绝其他所有。
3. 启用 SSL/TLS 加密传输(防止数据窃听)
MySQL 传输默认明文,需启用 SSL 加密客户端与服务器的通信(MySQL 5.7+ 支持,8.0+ 默认推荐)。
(1)生成 SSL 证书(自签或 CA 签发)
bash
# 生成 CA 证书(有效期 3650 天)
openssl genrsa 2048 > ca-key.pem
openssl req -new -x509 -nodes -days 3650 -key ca-key.pem > ca.pem
# 生成服务器证书
openssl req -newkey rsa:2048 -days 3650 -nodes -keyout server-key.pem > server-req.pem
openssl rsa -in server-key.pem -out server-key.pem
openssl x509 -req -in server-req.pem -days 3650 -CA ca.pem -CAkey ca-key.pem -set_serial 01 > server-cert.pem
# 生成客户端证书(供应用连接使用)
openssl req -newkey rsa:2048 -days 3650 -nodes -keyout client-key.pem > client-req.pem
openssl rsa -in client-key.pem -out client-key.pem
openssl x509 -req -in client-req.pem -days 3650 -CA ca.pem -CAkey ca-key.pem -set_serial 02 > client-cert.pem
# 修改证书权限(仅 mysql 用户可读)
chmod 600 *.pem
chown mysql:mysql *.pem
mv *.pem /etc/mysql/ssl/ # 移动到安全目录
(2)配置 MySQL 启用 SSL(my.cnf)
[mysqld]
ssl-ca = /etc/mysql/ssl/ca.pem
ssl-cert = /etc/mysql/ssl/server-cert.pem
ssl-key = /etc/mysql/ssl/server-key.pem
# 强制客户端使用 SSL 连接(可选,增强安全性)
require_secure_transport = ON # MySQL 8.0+ 支持;5.7 用 ssl=1
(3)验证 SSL 启用
sql
-- 服务器端验证
SHOW VARIABLES LIKE '%ssl%'; # 若 have_ssl 为 YES 则启用成功
-- 客户端连接验证(需携带证书)
mysql -u appuser -p -h 192.168.1.100 --ssl-ca=/etc/mysql/ssl/ca.pem --ssl-cert=/etc/mysql/ssl/client-cert.pem --ssl-key=/etc/mysql/ssl/client-key.pem
-- 连接后验证 SSL 状态
STATUS; # 查看 SSL 行是否为 Cipher in use: xxx
4. 禁用 LOAD DATA LOCAL INFILE(防止文件泄露)
LOAD DATA LOCAL INFILE 允许客户端读取服务器本地文件,可能被利用泄露敏感数据(如 /etc/passwd),禁用:
[mysqld]
local-infile = 0 # 服务器端禁用
[mysql]
local-infile = 0 # 客户端禁用
五、数据安全加固(保护数据不泄露、不丢失)
1. 启用 InnoDB 透明加密(TDE,MySQL 8.0.16+ 支持)
对数据文件加密,防止磁盘被盗导致数据泄露:
[mysqld]
innodb_encrypt_tables = ON # 全局启用表加密
innodb_encrypt_log = ON # 重做日志加密
innodb_encryption_threads = 4 # 加密线程数
innodb_encryption_rotate_key_age = 90 # 90 天轮换密钥
(1)创建加密密钥(需先设置密钥环插件)
sql
-- 启用密钥环插件(my.cnf 中配置)
[mysqld]
early-plugin-load-add = keyring_file.so
keyring_file_data = /var/lib/mysql-keyring/keyring # 密钥存储路径
-- 创建加密表(自动加密)
CREATE TABLE appdb.user (id INT, name VARCHAR(20)) ENGINE=InnoDB;
2. 敏感字段应用层加密
对密码、手机号、身份证号等敏感字段,在应用层加密后存储(避免数据库管理员直接查看):
- 推荐算法:AES-256-GCM(对称加密,需妥善保管密钥);
- 示例(MySQL 内置 AES 加密,仅作演示,建议应用层加密):
sql
-- 加密存储(密钥需保存在应用配置,而非数据库)
INSERT INTO appdb.user (id, phone) VALUES (1, AES_ENCRYPT('13800138000', 'app_aes_key_2025'));
-- 解密查询
SELECT id, AES_DECRYPT(phone, 'app_aes_key_2025') AS phone FROM appdb.user;
3. 定期备份 + 异地存储(防止数据丢失)
(1)备份策略
- 全量备份:每日 1 次(使用
mysqldump或 Percona XtraBackup); - 增量备份:每小时 1 次(结合二进制日志
binlog); - 备份文件加密存储(如用
gpg加密)。
(2)mysqldump 备份示例(Linux)
bash
# 全量备份(含结构+数据,--master-data 记录 binlog 位置,用于增量备份)
mysqldump -u root -p --all-databases --master-data=2 --single-transaction --quick --lock-tables=false > full_backup_$(date +%Y%m%d).sql
# 加密备份文件
gpg -c full_backup_20251113.sql # 输入密码加密,生成 .gpg 文件
# 复制到异地存储(如 FTP/OSS/S3)
scp full_backup_20251113.sql.gpg user@backup-server:/backup/mysql/
(3)关键要求
- 定期测试恢复流程(确保备份可用);
- 备份文件保留至少 30 天;
- 异地存储与生产环境物理隔离。
六、日志与审计加固(追溯安全事件)
1. 启用关键日志
(1)错误日志(记录启动 / 关闭 / 异常错误)
[mysqld]
log-error = /var/log/mysql/mysqld-error.log
log-error-verbosity = 3 # 详细日志级别
(2)慢查询日志(优化性能,间接防范恶意慢查询攻击)
[mysqld]
slow_query_log = ON
slow_query_log_file = /var/log/mysql/slow.log
long_query_time = 2 # 执行时间超过 2 秒的查询记录
log_queries_not_using_indexes = ON # 记录未使用索引的查询
(3)二进制日志(binlog,用于增量备份 + 操作追溯)
[mysqld]
server-id = 1 # 唯一服务器 ID(主从复制/增量备份必需)
log_bin = /var/log/mysql/mysql-bin # binlog 存储路径
binlog_format = ROW # 行级格式(更安全,避免 SQL 注入篡改日志)
expire_logs_days = 7 # binlog 保留 7 天(避免磁盘满)
2. 启用审计日志(MySQL 企业版 / Percona Server)
社区版 MySQL 无内置审计功能,需安装第三方插件(如 audit_log)或使用 Percona Server(内置审计插件):
# Percona Server 审计配置(my.cnf)
[mysqld]
plugin-load-add = audit_log.so
audit_log_file = /var/log/mysql/audit.log
audit_log_format = JSON # JSON 格式便于分析
audit_log_policy = ALL # 记录所有操作(可按需调整为 LOGINS/DDL/DML)
3. 日志文件权限保护
bash
# 修改日志目录权限(仅 mysql 用户可读)
chown -R mysql:mysql /var/log/mysql
chmod 700 /var/log/mysql
chmod 600 /var/log/mysql/*
七、配置文件加固(my.cnf/my.ini)
汇总核心加固配置(直接替换或追加到 [mysqld] 段):
[mysqld]
# 基础安全
bind-address = 192.168.1.100
local-infile = 0
symbolic-links = 0 # 禁用符号链接(防止目录遍历攻击)
max_connections = 1000 # 限制最大连接数(防止DoS攻击)
wait_timeout = 600 # 空闲连接超时 10 分钟
interactive_timeout = 600
# 密码策略(5.7+)
validate_password_length = 12
validate_password_policy = STRONG
validate_password_special_char_count = 1
# SSL 加密(8.0+)
ssl-ca = /etc/mysql/ssl/ca.pem
ssl-cert = /etc/mysql/ssl/server-cert.pem
ssl-key = /etc/mysql/ssl/server-key.pem
require_secure_transport = ON
# 数据加密(8.0.16+)
innodb_encrypt_tables = ON
innodb_encrypt_log = ON
early-plugin-load-add = keyring_file.so
keyring_file_data = /var/lib/mysql-keyring/keyring
# 日志配置
log-error = /var/log/mysql/mysqld-error.log
slow_query_log = ON
slow_query_log_file = /var/log/mysql/slow.log
long_query_time = 2
log_bin = /var/log/mysql/mysql-bin
binlog_format = ROW
expire_logs_days = 7
# 禁用危险功能
disable-log-bin = OFF # 启用 binlog(前面已配置,此处强调)
八、定期维护与监控
1. 定期安全检查
- 每月执行
mysql_secure_installation复查; - 每季度扫描 MySQL 漏洞(使用工具如 Nessus、OpenVAS);
- 每半年更新 MySQL 补丁(关注 Oracle 安全公告)。
2. 监控关键指标
- 异常登录:监控
Host为陌生 IP 的登录记录(查询mysql.general_log或审计日志); - 慢查询 / 大事务:通过
slow.log或监控工具(如 PMM、Zabbix)预警; - 权限变更:定期对比
mysql.user表,发现未授权的用户 / 权限变更。
3. 应急响应预案
- 若发现数据泄露:立即禁用可疑账号、切断公网访问、备份当前数据、追溯攻击源;
- 若遭遇暴力破解:临时封禁攻击 IP(防火墙 / 安全组)、修改所有账号密码、启用二次验证(如结合 PAM 插件)。
总结
MySQL 加固核心是 "最小权限 + 加密传输 + 数据保护 + 审计追溯 ",优先完成 mysql_secure_installation、防火墙限制、密码策略、禁用公网监听这 4 项基础操作,再逐步推进 SSL 加密、TDE 存储加密、审计日志等高级防护。
根据业务场景灵活调整(如内网数据库可简化 SSL 配置,但必须限制登录 IP;公网数据库需强制 SSL + 二次验证),定期复查加固效果,确保安全策略持续有效。