教程
🛡️

网络安全

Web 安全、渗透测试、密码学与安全合规的系统化教程。

MySQL 数据库安全加固:账户、权限、网络与配置

数据库往往是被攻破后的"最终目标"。这篇讲清 MySQL 的 root 账户保护、最小权限账号、禁止远程 root、移除测试库与匿名用户、安全配置项(local_infile、secure_file_priv 等)以及日志审计。

· 更新于 2026-07-19 阅读量 --

数据库是绝大多数业务的”金库”——应用被攻破后,攻击者最终目标几乎都是拖库。而很多 MySQL 实例的默认配置对内部人、对已经进内网的攻击者相当友好:root 能远程登录、有匿名用户、测试库没清、还能用 into outfile 直接写文件。

这篇把 MySQL 加固最该做的几项讲清,原则仍是”最小权限 + 最小暴露”。

所有操作仅用于你自有或已授权的数据库。改配置、删账户前务必备份,生产库操作避开业务高峰。

第一关:保护 root 账户

默认安装的 MySQL 有时 root 无密码或弱密码,且允许远程登录,这是最危险的组合。

-- 给 root 设强密码(8.0 用 ALTER USER)
ALTER USER 'root'@'localhost' IDENTIFIED BY '复杂密码不低于12位';
-- 禁止 root 远程登录:root 只允许本机
DELETE FROM mysql.user WHERE User='root' AND Host NOT IN ('localhost','127.0.0.1');
FLUSH PRIVILEGES;

关键原则:root 只从 localhost 连,应用用单独的低权限账号连库。绝不要让 root 从应用服务器或公网直连。

第二关:删除匿名用户与测试库

MySQL 默认可能带匿名用户(空用户名,任何密码都能登)和 test 库(任何人有完全权限),必须清掉:

-- 删除匿名用户(User 为空)
DELETE FROM mysql.user WHERE User='';
-- 删除 test 库
DROP DATABASE IF EXISTS test;
DELETE FROM mysql.db WHERE Db='test' OR Db='test\\_%';
FLUSH PRIVILEGES;

匿名用户是”隐形的门”——你以为没账号进不来,其实空用户名就能登。清掉它。同时确认 mysql.user 表里没有多余账户。

第三关:应用账号最小权限

应用连接数据库,绝不该用 root,也不该给 ALL PRIVILEGES。按库按权限授权:

-- 建一个只属于某库、只有增删改查的应用账号
CREATE USER 'appuser'@'10.0.0.%' IDENTIFIED BY '强密码';
GRANT SELECT, INSERT, UPDATE, DELETE ON shop.* TO 'appuser'@'10.0.0.%';
FLUSH PRIVILEGES;
-- 切忌:GRANT ALL ON *.* TO appuser@'%';

要点:

  • 账号绑定来源 IP/网段'appuser'@'10.0.0.%'),而不是 @'%' 任意主机。
  • 只给业务真正需要的权限,不要用 ALL、不要用 *.* 全库。
  • 不同业务用不同账号,一个泄露不影响全部。

第四关:网络暴露最小化

MySQL 默认监听 3306,很多运维图省事直接对外。正确做法:

  • my.cnf 里设 bind-address = 127.0.0.1,只允许本机连;应用和库同机或走内网。
  • 若必须远程,用防火墙/安全组只允许应用服务器 IP 访问 3306,绝不公网开放。
  • 改默认端口 port = 3307 只能降扫描命中率,不是根本安全,别依赖。
[mysqld]
bind-address = 127.0.0.1
port = 3306

第五关:关键安全配置项

my.cnf 里有几个和”被利用写文件/读文件”直接相关的开关,必须重视:

[mysqld]
# 禁止从客户端加载本地文件,防 LOAD DATA LOCAL 注入与数据外泄
local_infile = 0
# 限制 INTO OUTFILE / LOAD_FILE 只能读写这个目录,设为 NULL 则完全禁止
secure_file_priv = /var/lib/mysql-files/
# 不读取用户家目录下的配置文件,防提权
secure-auth = 1
# 跳过符号链接,防利用 symlink 访问任意文件
symbolic-links = 0

secure_file_priv 特别关键:很多”MySQL 写 Webshell”的利用,前提就是 secure_file_priv 为空(允许写任意目录)。设为特定目录能直接堵死这类利用。

第六关:日志与审计

开启日志便于事后排查异常查询和拖库行为:

[mysqld]
# 通用查询日志(调试用,生产慎用,量大)
general_log = 0
# 慢查询日志(可发现全表扫拖库)
slow_query_log = 1
long_query_time = 2
# 二进制日志(主从/恢复用,也是审计线索)
log-bin = mysql-bin

企业版有 MySQL Enterprise Audit,社区版可借助 init-connect + 审计表、或第三方插件(如 MariaDB Audit Plugin、Percona 的 audit_log)记录连接与语句。重点是:异常的大量 SELECT *、非业务时段的导出,能在日志里留下痕迹。

常见误区

  • 应用用 root 连库:一旦应用被注入,攻击者直接拿到数据库最高权限,等于没防。
  • @'%' 任意主机授权:账号可从任意 IP 连,配合弱口令就是公开入口。
  • secure_file_priv 留空:给”写文件/读文件”类利用大开方便之门。
  • 开了 general_log 又不清理:日志暴涨把磁盘写满,反而造成业务故障。
  • 只加固不备份:加固防外贼,备份防内灾(误删、勒索),两者都不可少。

进阶:用脚本核查配置

# 检查关键变量是否按基线设置
mysql -N -e "SHOW VARIABLES LIKE 'secure_file_priv';" 
mysql -N -e "SHOW VARIABLES LIKE 'local_infile';"
mysql -N -e "SELECT user,host FROM mysql.user WHERE user='';"  # 应为空

把这几条做成定时任务,输出偏离基线的项,就是最简单的”数据库基线核查”。

自测题

  1. 为什么 root 账户必须禁止远程登录?
  2. 匿名用户为什么危险?怎么清?
  3. secure_file_priv 设为 NULL 或特定目录,能防住哪类利用?
  4. 应用账号授权为什么要用 'appuser'@'10.0.0.%' 而不是 @'%'

实战要点与深度解析

MySQL 加固里最典型的”配置对了但依然危险”场景,是应用账号虽然低权限,却能从应用服务器直连数据库且密码写在了代码里。很多代码仓库(甚至公开到 GitHub 的)里硬编码着数据库密码,一旦仓库泄露,攻击者拿着密码从任意能连通 3306 的地方就能登进来。所以”账号绑定来源 IP”这一条格外重要:即使密码泄露,只要攻击者不在白名单网段,依然连不进来。更进一步,应把凭证放进配置中心/密钥管理服务,而不是明文写在代码或配置文件里。

再讲一个进阶点:performance_schema 与审计的取舍。社区版 MySQL 没有企业级审计插件,但你可以利用 init-connect 参数:让每个连接先执行一条 INSERT 把”谁、从哪 IP、什么时间连的”写进一张审计表。虽然不如专业审计插件强大,但能以极低成本拿到”连接溯源”能力,配合慢查询日志,足以发现”非业务时段的大量连接""异常来源 IP”等信号。当然,这张审计表本身也要保护好,防止被攻击者清空。

关于 主从复制与加固的关系:很多生产库是主从架构,从库用于读写分离或备份。这里有个隐患:从库往往被配成”信任主库、权限较松”,且可能对外开放供报表系统读取。如果从库安全水位低,攻击者拿下从库后能读到全量数据,危害不亚于主库。所以主从都要按同一基线加固,尤其从库也不能用弱口令、也不能随意暴露。

还有一个容易被忽略的配置:skip-grant-tables 绝不能留在生产配置里。这个参数会让 MySQL 跳过所有权限验证,任何人都能以管理员身份登入——它是”忘密码时的急救开关”,若误留在配置中,等于给数据库开了无锁后门。每次排障用完必须立即移除并重启。

最后提醒:数据库加固和备份是双胞胎。前面所有账户、权限、网络加固,防的是”外部/内部人员非法进入”;但如果是勒索软件加密了数据文件、或误操作 DROP 了表,加固再多也救不回来。所以 mysqldump/物理备份 + 异地/离线保留 + 定期恢复演练,必须和加固同步做。很多单位”库很安全但没备份”,一次误删就酿成事故,这个教训值得反复强调。

速查清单与排错口诀

MySQL 加固口诀:root 本、匿测清、权最小、源要限、文件锁、志要记、备同行。即 root 只本机、清匿名与测试库、权限最小且限来源、锁文件读写、开日志、备份与加固同行。

排错场景:设了 bind-address=127.0.0.1 后应用连不上库。这通常是应用和库不在同一台、却忘了把应用服务器 IP 加进账号的授权主机(如 'appuser'@'10.0.0.%'),或防火墙没放通。正确做法是”库本机监听 + 应用账号限定来源网段 + 防火墙仅放行该网段”,三层一致才通。另一个坑:把 secure_file_priv 设成目录后,某合法导出功能(如报表生成)失效——这时应把导出路径改到该允许的目录,而不是退回”允许任意目录”。加固和业务的冲突,永远用”换路径/换方式”化解,而不是撤掉安全配置。

进阶速记与误区辨析

MySQL 加固里也有几组容易让人掉坑的概念,专门辨析一下。

第一组,root 禁远程与运维便利性。禁用 root 远程登录确实更安全,但很多运维为了方便,又偷偷建了一个权限几乎等同 root 且允许任意来源连接的账户,结果只是把风险换了个名字。真正的做法是应用用受限账户、来源限定网段、权限只给业务所需,不能明面上禁了 root 暗地里又开了个超级账户。

第二组,强密码与凭证保管。设了强密码只是第一层,如果密码被明文写在代码仓库或者配置文件里,仓库一泄露密码就跟着泄露,强密码形同虚设。凭证应该放进配置中心或者密钥管理服务,代码里只留引用,不能把密码当普通字符串到处写。

第三组,限制来源与网络暴露。给应用账户限定了来源网段,但如果数据库本身还监听在公开地址且防火墙没拦,攻击者依然可能从别的路径摸到。所以限制来源要和应用侧的网络隔离、防火墙规则配合起来,三层一致才真正堵住入口。

第四组,安全参数与正常功能。把文件读写参数限制到指定目录能挡住很多写文件类利用,但某些合法的导出功能可能正依赖更宽的路径。这时不能简单退回”允许任意目录”,而应该把导出功能改到允许的目录里去,用改路径而不是撤安全来化解冲突。

速记口诀收尾:root 本机、匿名清掉、权限最小、来源要限、文件锁死、日志要记、备份同行。七句话把 MySQL 加固的主线串起来,配合前面的实战章节,基本能覆盖日常绝大多数场景。

这一篇你该记住的

  • root 只从 localhost 连,设强密码,绝不让远程直连。
  • 删除匿名用户与 test 库,清掉”隐形门”。
  • 应用账号最小权限、绑定来源 IP、禁止 ALL ON *.*
  • bind-address=127.0.0.1 + 防火墙仅放行应用 IP,3306 不暴露公网。
  • local_infile=0secure_file_priv 限制目录,堵死写/读文件利用。
  • 开慢查询/二进制日志做审计线索,且加固同时务必做备份。

下一篇我们看 Web 服务器 Nginx 的安全加固。