如果你装完 MySQL 的第一反应是直接连上去跑业务我建议你先停一下。刚安装好的 MySQL 默认状态下其实是一个“能跑但很不安全”的状态root 可能没有密码或密码很弱、系统里存在不必要的匿名账号、root 默认允许从任意主机登录、还有一套谁都能访问的 test 测试库。这些默认配置在开发机上也许无所谓但一旦部署到生产环境就是送上门的安全缺口。mysql_secure_installation 这个安全脚本就是为了收拾这个烂摊子而生的。它用一连串交互式提问把上面这些隐患逐个清除掉全程不用手写 SQL。这篇内容我会把它的执行过程拆开揉碎讲清楚每一步到底在干什么、背后执行了什么操作、有哪些坑以及如何在自动化场景下安全引入它。1. 为什么要给 MySQL 做“初始安全收尾”1.1 默认安装留下的风险点MySQL 安装完成后初始化流程会创建一套初始账号体系。不同版本、不同安装方式包管理器、二进制包、Docker 镜像生成的默认状态不完全一样但常见的隐患大致是这几类root 账号没有设置有效密码或使用的是临时密码容易被爆破。系统中存在匿名账号任意本地用户不需要身份验证就能连进数据库。root 账号的主机授权范围过宽比如 root%意味着只要有网络权限就能从任意 IP 尝试登录。自动创建的 test 数据库及配套的权限默认对所有人开放容易成为数据泄露的试验场。这些情况单独看好像没什么组合在一起就是灾难。以前我遇到过一台刚部署好的测试数据库被人扫到了 3306 端口一群人开始轮番尝试弱口令日志里全是失败的连接记录随便翻两页就能看到针对 root 的攻击尝试。要是当时没做过初始加固后果可想而知。这也是我建议所有刚接触 MySQL 的人装完数据库之后别急着建库建账号先花几分钟把 mysql_secure_installation 跑一遍的原因。它不一定能让数据库变成铜墙铁壁但能帮你把默认状态下最明显、最容易被利用的漏洞一次性堵掉。1.2 为什么不用手写 SQL 代替你可能会想这些操作不就是几条 DELETE、DROP、ALTER USER 吗我手动执行不就行了何必依赖脚本。确实可以但前提是你很清楚每条语句涉及哪些系统表、哪些账号、哪些授权关系。手动操作最怕两件事一是漏掉某个匿名用户或残留授权二是对全局权限表执行了过于激进的清理导致数据库账号体系直接崩掉。脚本的优势在于它封装了完整的逻辑每一步都经过 MySQL 官方团队的整理交互式提问也会让你确认后再执行误操作的概率低得多。另外脚本在交互过程中还会用到 MySQL 自带的安全策略校验。比如当你设置 root 密码时如果启用了密码校验插件它可以帮你把弱密码拦在前面避免你给自己留一个“123456”级别的后门。这个能力手动写 SQL 也能触发但脚本会在一开始就引导你走完整的流程体验更顺畅。2. 执行前的准备工作版本、权限与备份2.1 确认脚本路径和 MySQL 版本mysql_secure_installation 是 MySQL 官方自带的一个 shell 脚本安装 MySQL 服务端时会一并装好。它一般位于 MySQL 安装目录的 bin 文件夹下比如 /usr/bin/mysql_secure_installation或者通过 mysql_secure_installation 命令名直接调用。在执行之前我习惯先确认一下 MySQL 服务端的版本因为不同版本对密码策略、验证组件的处理方式不一样脚本的输出也会有点差别。可以用这条命令看版本mysql --version也可以连进 MySQL 后用 SQL 查SELECT VERSION();脚本本身对 MySQL 5.7 和 8.0 都适用但 8.0 里密码验证组件默认启用脚本中关于密码强度的提示会更明显而 5.7 的安装方式里密码校验组件可能是可选的。这一点会直接影响你设置密码时能使用的字符组合建议先心里有数。2.2 用哪种身份执行mysql_secure_installation 的核心任务是修改 MySQL 系统账号表所以它必须拥有足够的权限。通常有两种执行方式直接用系统 root 用户执行sudo mysql_secure_installation用具备 sudo 权限的普通用户执行脚本连接 MySQL 时默认会以 root 身份连接本地实例。如果你的系统 root 与 MySQL root 是一套账户体系例如使用了 auth_socket 认证插件那么直接 sudo 执行后就能免密进入交互流程。如果 MySQL root 设置了独立密码脚本会要求你输入密码后再继续。有一点要特别注意系统 root 和 MySQL root 并不是一回事系统 root 是操作系统层面的管理员MySQL root 是数据库实例的管理员。脚本利用的是系统权限来读取配置、连接本地 socket真正修改权限表靠的是 MySQL root 的数据库权限。2.3 备份与执行窗口虽然 mysql_secure_installation 主要是清理默认账号和测试库不会动业务数据但它涉及权限表的修改而且如果 root 密码为空或过弱脚本会强制你修改密码。这个动作可能影响到依赖旧密码的连接配置。所以我对生产环境的建议是在维护窗口内执行。执行前备份用户表和权限相关的关键信息。备份方式很简单不需要全量备份整个实例只需要把 mysql 库里的 user、db、tables_priv、columns_priv 等权限相关表的信息导出来留档mysqldump -uroot -p mysql user db tables_priv columns_priv /tmp/mysql_privileges_backup.sql万一执行过程中出现误操作可以用这个备份快速恢复权限体系。大多数情况用不上但留一手总比事后拍大腿强。3. 交互式执行全流程白盒拆解3.1 启动脚本与 root 密码处理当你敲下 mysql_secure_installation 之后脚本会先检查能否连接本地 MySQL 实例然后进入交互流程。一般情况下你会看到类似这样的输出Securing the MySQL server deployment. Enter password for user root:这一步要求输入的密码是 MySQL root 用户的密码而不是服务器系统 root 的密码。如果之前没有设置过 root 密码直接回车即可如果设置过正确输入后会继续后续步骤。随后脚本会询问是否设置 root 密码The validate_password component is installed on the server. Please provide a password for root here. New password: Re-enter new password:如果你安装的是 MySQL 8.0这里会有密码强度策略生效。脚本会让你设置一个符合策略的密码策略通常分为低只检查密码长度。中检查长度、大小写字母、数字、特殊字符的组合。高在中等的基础上还会检查密码中是否包含字典里的常见词语。这里我不建议为了省事选低强度除非是临时测试环境。数据库 root 密码一旦泄露整个实例就相当于门户大开密码本身应该用随机字符串生成别用自己的姓名、生日之类的内容。3.2 移除匿名用户设置完密码后脚本会问Remove anonymous users? [Y/n]默认是 Y。我建议直接回车同意。匿名用户指的是用户名为空且主机匹配规则很宽泛的账号。MySQL 在验证身份时如果客户端提供的用户名在用户表里没有精确匹配有可能落到匿名用户条目上从而跳过密码验证。这种情况在旧版本的默认安装里尤其常见。脚本在内部执行的操作大致是清理 mysql.user 表中用户名为空的记录并回收相关授权。你可以把它理解为把数据库后门上锁。3.3 禁止 root 远程登录接下来是Disallow root login remotely? [Y/n]这句话的意思是是否禁止 root 账号从非本机地址远程登录。选择 Y 之后脚本会把 root 账号的主机限定为 localhost只允许在本机通过 socket 或回环地址登录。在 MySQL 的账号体系里一个用户可能对应多条记录比如 rootlocalhost、root127.0.0.1、root%。如果存在 root% 这条记录意味着任何人都可以尝试从外网用 root 身份连接。脚本做的就是删除这类非本机的 root 授权。有些朋友会担心我选择 Y 之后应用服务器怎么连接数据库这里要说清楚线上应用的连接不应该用 root而应该使用按需创建的应用程序账号并只授予它访问业务库的必要权限。root 远程登录权限关闭后管理员依然可以在服务器本机登录管理实例。3.4 删除测试数据库脚本接着问Remove test database and access to it? [Y/n]对 MySQL 来说test 数据库是默认存在的“沙盒”。它的问题在于默认授权里test 库通常允许任意用户访问甚至允许在 test 前缀的库上执行创建、写入等操作。这在一个安全的数据库实例里是完全不必要的。选择 Y 后脚本会删除 test 库并移除对它的访问授权。如果你的业务库里恰好有名为 test 或类似前缀的库且确实需要保留那就要谨慎了。脚本主要处理的是官方默认创建的 test 库一般不会误伤业务库但为了保险执行前还是先看一眼SHOW DATABASES;3.5 重新加载权限表最后一个交互确认是Reload privilege tables now? [Y/n]这一步要执行的是 FLUSH PRIVILEGES作用是让前面所有对 mysql.user、mysql.db 等系统表的修改立即生效。MySQL 在正常运行时会在内存里缓存权限数据直接改表不会立刻作用到当前连接和后续新连接上只有执行 FLUSH PRIVILEGES 才会强制刷新。选择 Y 之后脚本会输出... Success! All done!到这里一次标准的交互式安全加固就完成了。4. 常用参数与自动化执行方案4.1 看懂脚本的可选参数mysql_secure_installation 在交互模式下可能需要你手动确认好几遍在批量部署或自动化初始化场景下不太方便。脚本本身也提供了一些参数来减少交互次数常用的是这几个参数作用--use-default所有提问都采用默认答案不等待输入--skip-password跳过密码输入步骤适合 root 密码已被外部设置的场景--defaults-file指定 MySQL 配置文件路径脚本读取客户端连接参数时使用如果只是想尽快跑完默认加固流程而且确认自己对默认选项没有异议可以用mysql_secure_installation --use-default这个参数会把所有问题按默认值直接确认包括移除匿名用户、禁止 root 远程登录、删除测试库、刷新权限表等。唯一的例外是密码设置环节。如果没有提前把 root 密码处理干净脚本还是会在设置密码这里卡住。我在使用 --use-default 时一般会配合 --skip-password 一起用前提是 root 密码已经预先通过其他方式配置好了。这样能实现真正意义上的无交互执行。4.2 自动化脚本的几种写法在初始化数据库实例时常见做法不是让脚本全程交互而是用预先写好的命令组合来完成同样的事情。下面这个方案是我在实际部署中经常用的逻辑完全不依赖交互输出# 启动 MySQL 后先确认服务在监听 mysqladmin -uroot status # 设置 root 密码在密码为空的情况下 mysql -uroot --skip-password -e ALTER USER rootlocalhost IDENTIFIED BY 你的安全密码; # 移除匿名用户 mysql -uroot -p你的安全密码 -e DELETE FROM mysql.user WHERE User; # 禁止 root 远程登录只保留本机 mysql -uroot -p你的安全密码 -e DELETE FROM mysql.user WHERE Userroot AND Host NOT IN (localhost, 127.0.0.1, ::1); # 删除 test 库 mysql -uroot -p你的安全密码 -e DROP DATABASE IF EXISTS test; # 刷新权限 mysql -uroot -p你的安全密码 -e FLUSH PRIVILEGES;这段命令本质上就是把 mysql_secure_installation 的每一步用 SQL 复现了一遍。好处是可控性更强每一步做了什么清清楚楚坏处是没有脚本自带的密码强度校验保护你需要自己保证密码强度。如果希望保留脚本的校验逻辑又想自动化也可以借助 expect 之类的工具模拟交互输入。不过这种方式对脚本输出的变化比较敏感一旦 MySQL 升级后输出文本有调整expect 匹配就会失效。相较而言我更推荐用上面的 SQL 组合方案简单直接维护成本低。5. 实际执行中的典型问题与排查思路5.1 提示无法连接本地 MySQL 服务执行脚本后可能马上出现Connecting to MySQL using a blank password. ERROR: Cant connect to the local MySQL server through socket /var/run/mysqld/mysqld.sock原因通常是 MySQL 服务没有启动或者 socket 文件路径与配置不一致。先用系统服务命令确认状态比如systemctl status mysqld如果服务没启动先启动再执行。如果服务是启动状态那大概率是 socket 路径问题检查 /etc/my.cnf 或 /etc/mysql/ 下的配置确认 socket 指向的路径是否存在。有些 Docker 容器场景下 socket 文件可能挂载在特定目录需要脚本在容器内执行而不是在宿主机上执行。5.2 提示密码错误但实际上密码是对的这个现象在装了 MySQL 8.0 的服务器上很常见。脚本连接 MySQL 时用的是 root 账号但如果 root 账号使用了 auth_socket 认证插件比如很多 Linux 发行版默认就是这样配置那么通过 TCP/IP 方式提供密码是没用的只有系统 root 用户通过 socket 登录才能认证。解决办法是先用系统 root 身份进入 MySQL确认一下 root 账号的插件类型SELECT user, host, plugin FROM mysql.user WHERE userroot;如果 plugin 是 auth_socket就保持这样即可脚本用 sudo 执行时应该能正常通过。如果 MySQL root 的插件是 caching_sha2_password那就要输入正确的密码。5.3 密码强度校验报错设置新密码时如果看到类似 ERROR 1819说明密码不符合 validate_password 组件的策略要求。这类错误信息通常会直接告诉你问题出在哪ERROR 1819 (HY000): Your password does not satisfy the current policy requirements处理方式有两种要么换一个符合要求的复杂密码要么临时调整策略等级。测试环境想快速通过可以进入 MySQL 后执行SET GLOBAL validate_password.policyLOW; SET GLOBAL validate_password.length8;但生产环境不建议降低策略还是老老实实用强密码。另外别忘了如果是 8.0修改这些变量可能还需要考虑参数在重启后是否持久化最好用 SET PERSIST 而不是 SET GLOBAL。5.4 自动化流程里脚本非正常退出非交互模式下一个常见坑是脚本因为某个交互问题卡住而 CI 任务又设置了超时时间导致部署流程失败。这里我建议不要在自动化环境里强行依赖这个脚本尤其是密码策略、扩展选项在不同版本间有差异的环境。更稳妥的做法是把脚本作为人工执行的初始化步骤自动化流程则用我们前面提到的 SQL 组合或基础设施工具来完成。有些配置管理工具也有封装好的 MySQL 初始化逻辑可以指定 root 密码、删除匿名用户、移除 test 库等效果和脚本完全一样但每一步都能看到明确的执行状态。6. 跑完脚本不等于万事大吉后续加固自查清单6.1 核对账号体系是否正常脚本执行结束后我会习惯性做一次账号列表核对确保没有漏网之鱼SELECT user, host, plugin FROM mysql.user ORDER BY user, host;正常情况下的输出应该是一份精简的列表比如---------------------------------------------------- | user | host | plugin | ---------------------------------------------------- | root | localhost | auth_socket | | wordpress_user | localhost | caching_sha2_password | ----------------------------------------------------如果看到 User 字段为空的记录或者 Host 为 % 的非预期授权要立刻处理。还要顺手看一眼端口监听范围确保 MySQL 没有暴露在完全公开的网络接口上。查看监听情况可以用ss -lntp | grep 3306如果监听地址是 0.0.0.0而且数据库前面没有防火墙或安全组限制那就得重视起来了。6.2 权限审计与最小化授权mysql_secure_installation 能清理默认账号但没法替你做业务上的权限规划。很多安全问题不是源于初始配置而是源于业务上线后授权过于随意。比如一个只需要读订单表的应用账号却被授予了 ALL PRIVILEGES或者所有应用共用一个数据库账号出了问题根本没法定位是谁干的。我的建议是坚持最小化授权原则一个应用一个账号账号只拥有它业务所必需库表的权限。可以定期执行下面这条 SQL把当前所有账号的权限导出来看看SELECT user, host, Grant_priv, Super_priv FROM mysql.user;然后再针对具体账号执行SHOW GRANTS FOR app_userlocalhost;遇到满屏 ALL PRIVILEGES 的账要重新梳理。刚开始实施的时候可能会觉得有点繁琐但数据库账号体系清晰之后排障和审计都会轻松很多。6.3 关注日志与后续变更脚本加固之后MySQL 的错误日志和审计日志是判断实例健康度的重要窗口。尤其是从默认状态到安全状态的过渡阶段可能会有旧的连接配置因为密码变更而失败应用日志里会冒出 Access denied 之类的错误。这时候别慌按应用使用的账号逐一排查把密码更新到应用的配置中心。如果你担心 MySQL 实例被扫描或爆破还可以考虑启用失败连接日志或审计插件。不过审计插件在高并发场景下可能带来额外的性能开销启用前先做个简单的压测对比别为了安全而牺牲可用性。在实际操作中我的习惯是把 mysql_secure_installation 的执行过程沉淀成一份初始化操作单放在数据库部署文档里。每次搭建新实例照着操作单跑一遍再结合自己的账号规划做二次清理。这种方法看似简单但能避免很多日后才暴露的问题。安全加固从来不是某一次执行的终点而应该成为新环境上线时必须跨过的一道门槛。
阅读完成 · 觉得有帮助?