适用场景
MySQL 作为最流行的开源关系型数据库,广泛用于 Web 应用、CMS 系统和企业级业务系统。然而,默认安装的 MySQL 存在大量安全隐患:root 账户无密码或弱密码、匿名账户未删除、测试数据库未清理、网络监听范围过大、未启用 SSL 加密传输等。本教程适用于运维工程师、安全管理员和开发者在生产环境中对 MySQL 进行全面的安全加固。
前置条件
- 已安装 MySQL 5.7+ 或 MySQL 8.0+(本教程以 MySQL 8.0 为例)
- 拥有 MySQL root 权限或 sudo 权限
- 操作系统:CentOS 7+/Ubuntu 20.04+
- 具备基本的 Linux 命令行操作能力
原理说明
MySQL 安全加固的核心原则是”最小权限 + 纵深防御”。最小权限原则要求每个数据库用户只拥有完成其任务所需的最小权限集合;纵深防御则要求在操作系统层、网络层、应用层和数据库层同时部署安全措施。本教程从账户安全、网络配置、访问控制、传输加密、审计日志和补丁管理六个维度构建完整的防护体系。
操作步骤
步骤 1:运行安全安装脚本
MySQL 自带的 mysql_secure_installation 脚本可一键完成大部分基础安全配置:
# 运行安全安装脚本
sudo mysql_secure_installation
# 按提示依次操作:
# 1. 设置 root 密码(输入 VALIDATE PASSWORD COMPONENT)
# 2. 删除匿名用户 → Y
# 3. 禁止 root 远程登录 → Y
# 4. 删除 test 数据库 → Y
# 5. 重新加载权限表 → Y
步骤 2:绑定本地监听地址
默认 MySQL 监听 0.0.0.0:3306,这意味着任何网络可达的主机都可尝试连接。将监听地址限制为 127.0.0.1:
# 编辑 MySQL 配置文件
sudo vim /etc/mysql/mysql.conf.d/mysqld.cnf # Ubuntu/Debian
# 或
sudo vim /etc/my.cnf # CentOS/RHEL
# 找到或添加以下行
bind-address = 127.0.0.1
# 重启 MySQL 服务
sudo systemctl restart mysql
如果应用服务器和数据库分离部署(不在同一台机器),则将 bind-address 设置为应用服务器的内网 IP,切勿使用 0.0.0.0。
步骤 3:移除默认数据库和匿名账户
清理 MySQL 默认安装中的安全风险:
# 登录 MySQL
sudo mysql -u root -p
# 查看当前用户列表
SELECT User, Host, authentication_string FROM mysql.user;
# 删除匿名用户(User 为空的账户)
DROP USER IF EXISTS ''@'localhost';
DROP USER IF EXISTS ''@'$(hostname)';
# 删除测试数据库
DROP DATABASE IF EXISTS test;
# 删除对 test 数据库的默认访问权限
DELETE FROM mysql.db WHERE Db LIKE 'test%';
# 应用变更
FLUSH PRIVILEGES;
步骤 4:创建专用应用账户(最小权限原则)
不要使用 root 账户连接数据库。为每个应用创建专用账户并授予最小权限:
-- 创建应用账户(仅允许从 localhost 连接)
CREATE USER 'myapp'@'localhost' IDENTIFIED BY 'YourStrongP@ssw0rd!';
-- 仅授予应用所需数据库的权限
GRANT SELECT, INSERT, UPDATE, DELETE ON myapp_db.* TO 'myapp'@'localhost';
-- 如需远程连接(从应用服务器),指定应用服务器内网 IP
CREATE USER 'myapp'@'192.168.1.%' IDENTIFIED BY 'YourStrongP@ssw0rd!';
GRANT SELECT, INSERT, UPDATE, DELETE ON myapp_db.* TO 'myapp'@'192.168.1.%';
FLUSH PRIVILEGES;
-- 查看账户权限
SHOW GRANTS FOR 'myapp'@'localhost';
步骤 5:启用密码策略
MySQL 8.0 内置了密码验证组件,可强制实施密码复杂度:
-- 查看当前密码策略
SHOW VARIABLES LIKE 'validate_password%';
-- 设置密码策略等级(0=低, 1=中, 2=高)
SET GLOBAL validate_password.policy = 1; -- MySQL 8.0
# 或
SET GLOBAL validate_password_policy = MEDIUM; -- MySQL 5.7
-- 设置密码最小长度
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;
要使设置永久生效,请在 my.cnf 的 [mysqld] 段添加:
[mysqld]
validate_password.policy = 1
validate_password.length = 12
validate_password.mixed_case_count = 1
validate_password.number_count = 1
validate_password.special_char_count = 1
步骤 6:启用 SSL/TLS 加密传输
在非信任网络中,不加密的 MySQL 连接可能被中间人窃听。启用 SSL 加密:
# 生成自签名证书(或使用 CA 签发证书)
sudo mysql_ssl_rsa_setup --uid=mysql
# 在 my.cnf 中启用 SSL
[mysqld]
ssl_ca = /var/lib/mysql/ca.pem
ssl_cert = /var/lib/mysql/server-cert.pem
ssl_key = /var/lib/mysql/server-key.pem
# 重启 MySQL
sudo systemctl restart mysql
# 验证 SSL 状态
mysql -u root -p -e "SHOW VARIABLES LIKE '%ssl%';"
强制要求 SSL 连接:创建用户时指定 REQUIRE SSL:
ALTER USER 'myapp'@'192.168.1.%' REQUIRE SSL;
FLUSH PRIVILEGES;
步骤 7:启用审计日志
MySQL Enterprise Audit 或通用的通用日志可用于记录数据库操作:
-- 启用通用查询日志(开发环境)
SET GLOBAL general_log = ON;
SET GLOBAL general_log_file = '/var/log/mysql/query.log';
-- 【推荐】启用慢查询日志,记录执行时间过长的查询
SET GLOBAL slow_query_log = ON;
SET GLOBAL slow_query_log_file = '/var/log/mysql/slow.log';
SET GLOBAL long_query_time = 2; -- 超过2秒的查询
-- 在 my.cnf 中永久配置
[mysqld]
slow_query_log = 1
slow_query_log_file = /var/log/mysql/slow.log
long_query_time = 2
log_queries_not_using_indexes = 1
步骤 8:限制错误登录尝试
安装 connection_control 插件来限制失败登录次数:
-- 安装插件
INSTALL PLUGIN connection_control SONAME 'connection_control.so';
INSTALL PLUGIN connection_control_failed_login_attempts SONAME 'connection_control.so';
-- 设置:连续失败 3 次后,每次尝试延迟 1 秒
SET GLOBAL connection_control_failed_connections_threshold = 3;
SET GLOBAL connection_control_min_connection_delay = 1000; -- 毫秒
-- 查看失败登录记录
SELECT * FROM information_schema.CONNECTION_CONTROL_FAILED_LOGIN_ATTEMPTS;
配置验证
完成以上配置后,逐一验证加固效果:
验证 1:绑定地址 netstat -tlnp | grep 3306 # 期望输出:127.0.0.1:3306 或内网IP:3306,绝不能是 0.0.0.0:3306 验证 2:匿名账户 mysql -u root -p -e "SELECT User,Host FROM mysql.user WHERE User='';" # 期望输出:空(无匿名用户) 验证 3:密码策略 mysql -u root -p -e "SHOW VARIABLES LIKE 'validate_password%';" 验证 4:SSL 连接测试 mysql -u myapp -p -h 127.0.0.1 --ssl-mode=REQUIRED -e "STATUS;" # 期望输出中 SSL 显示为 "Cipher in use is ..." 验证 5:外部连接限制 # 从另一台机器测试(应被拒绝) mysql -u root -p -h-e "SELECT 1;" # 期望:Access denied for user(root 远程登录被禁止)
常见问题
Q1:设置了 bind-address 后应用无法连接 MySQL,如何排查?
首先确认应用配置中的数据库连接地址是否为 127.0.0.1。如果应用与数据库在同一台机器,修改应用配置的 host 为 127.0.0.1。如果分离部署,bind-address 应设为应用服务器的内网 IP。同时检查防火墙规则是否放行了 3306 端口:sudo ufw allow from <app_ip> to any port 3306。
Q2:启用密码策略后,修改密码总会报错”password does not satisfy”,如何处理?
检查当前密码策略要求,确保新密码包含大小写字母、数字、特殊字符且长度不低于配置值。建议使用密码管理器生成复杂度足够的密码。如需临时降低策略,将 validate_password.policy 设为 0(LOW),完成密码修改后再恢复。
总结
MySQL 安全加固是一项持续性的工作,本教程覆盖了从基础配置到进阶防护的八个关键步骤:安全安装脚本、绑定地址限制、清理默认账户、最小权限账户、密码策略、SSL 加密、审计日志和登录限制。建议将这些配置纳入服务器初始化脚本或配置管理工具(如 Ansible)中,确保新建数据库实例自动继承安全基线。最后,定期(建议每月)检查 MySQL 安全公告,及时更新至最新补丁版本。