应绕过 mysql_secure_installation 的交互限制,用 mysql 命令行执行 SQL 批量加固:提取临时密码登录后改密、删匿名用户和 test 库、刷新权限;同步修改 mysqld.cnf 配置 bind-address、local_infile、sql_mode 等;创建用户时禁用 '%' 和 GRANT ALL,限定主机与最小权限;MySQL 8.0+ 注意认证插件;配合防火墙规则、降权运行、目录与配置文件权限收紧,并持久化 validate_password 插件及密码策略。

直接在安装脚本里调用 mysql_secure_installation 不可靠——它依赖交互式输入,自动化脚本会卡住。必须绕过交互,用非交互方式完成等效加固。
用 mysql 命令行批量执行安全语句替代交互脚本
脚本中不能写 mysql_secure_installation 并期望它自动完成,因为该工具默认阻塞等待用户输入。实际做法是:启动 mysqld 后,用临时密码登录,然后用 mysql -u root -p --connect-expired-password 配合 here-document 或管道执行一连串 SQL。
- 先从日志提取初始密码:
grep "temporary password" /var/log/mysqld.log | awk '{print $NF}' - 登录后立刻改密并禁用空密码:
ALTER USER 'root'@'localhost' IDENTIFIED BY 'StrongPass!2026'; - 删匿名用户:
DELETE FROM mysql.user WHERE User=''; - 删 test 库:
DROP DATABASE IF EXISTS test; - 刷新权限:
FLUSH PRIVILEGES;
配置文件中预设关键安全项(mysqld.cnf)
光靠 SQL 语句不够,很多限制必须写进配置文件才生效,且重启后持续有效。脚本应在安装后自动修改或覆盖配置段。
-
bind-address = 127.0.0.1:除非明确需要远程访问,否则强制本地监听 -
skip-networking = OFF(确保没被意外开启) -
local_infile = OFF:防止恶意利用LOAD DATA LOCAL INFILE -
sql_mode = STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION:减少宽泛模式带来的注入风险 - 若需密码强度控制,追加:
plugin-load-add = validate_password.so和validate_password_policy = STRONG
创建专用用户时避免 GRANT ALL 和通配符主机
脚本里如果自动生成应用用户,常见错误是写成 GRANT ALL ON *.* TO 'app'@'%'——这等于开放全部权限和任意来源,生产环境绝对禁止。
- 主机限定必须具体:
'app'@'10.0.2.5'或'app'@'localhost',禁用'%'除非有反向代理或跳板机场景 - 权限按需授予:
GRANT SELECT, INSERT, UPDATE ON myapp_db.* TO 'app'@'localhost' - 显式拒绝危险权限:
REVOKE FILE, PROCESS, SUPER ON *.* FROM 'app'@'localhost' - MySQL 8.0+ 注意认证插件:
IDENTIFIED WITH caching_sha2_password BY 'xxx',别漏掉
防火墙与系统级权限必须同步处理
数据库层加固做完,不代表端口就安全了。脚本若运行在 Ubuntu/Debian 上,应调用 ufw;CentOS/RHEL 则用 firewall-cmd,且必须在 MySQL 启动前完成规则设置。
- Ubuntu 示例:
ufw allow from 192.168.10.0/24 to any port 3306,而不是ufw allow 3306 - 检查 MySQL 进程是否以低权限用户运行:
ps aux | grep mysqld | grep -v grep,确认属主是mysql而非root - 数据目录权限必须收紧:
chown -R mysql:mysql /var/lib/mysql和chmod 700 /var/lib/mysql - 配置文件本身也要限权:
chmod 600 /etc/mysql/mysql.conf.d/mysqld.cnf
最容易被忽略的是密码策略的持久化:validate_password 插件一旦加载,就必须在配置文件里声明,否则服务重启后失效;而 default_password_lifetime 这类全局变量,仅靠 SQL 设置无法跨重启保留,必须写进 [mysqld] 段。


















