mysql无原生权限表只读机制,防篡改需权限最小化、操作隔离与审计兜底;8.0+权限实时生效,flush privileges已降级为兼容指令;应禁用直改mysql.user,启用super_read_only和audit_log并提前固化策略。

mysql.user 表被改了怎么办
直接结论:MySQL 没有原生的「权限表只读锁定」机制,mysql.user、mysql.db 等系统表本质上就是普通 MyISAM 或 InnoDB 表,只要拥有 UPDATE 或 INSERT 权限的账号就能改——包括你自己误操作。
所以防篡改不是靠“锁表”,而是靠「权限最小化 + 操作隔离 + 审计兜底」。
用 FLUSH PRIVILEGES 之前先确认权限来源
很多人以为改完 mysql.user 就立刻生效,其实不会。MySQL 启动或执行 FLUSH PRIVILEGES 才加载权限缓存。但问题来了:如果你用的是 MySQL 8.0+,mysql.user 已是 InnoDB 表,而 FLUSH PRIVILEGES 在某些场景下反而会掩盖权限不一致问题(比如缓存未刷新但表已改)。
- MySQL 5.7 及更早:MyISAM 表,
FLUSH PRIVILEGES是必须的,但无法防止并发写入 - MySQL 8.0+:InnoDB 表,权限变更实时生效(除部分动态权限),
FLUSH PRIVILEGES实际上已降级为兼容性指令,多数情况可省略 - 真正生效时机取决于:是否启用
skip-grant-tables、是否用CREATE USER/GRANT而非直改表、以及是否重启 mysqld
禁止直接 UPDATE mysql.user 的实操控制点
最有效的方式不是技术封堵,而是权限和流程卡口:
- 所有 DBA 账号不赋予
UPDATE、DELETE权限在mysql库上,只给SELECT(查)和INSERT(仅限CREATE USER类操作) - 禁用 root 远程登录,本地 root 也限制只能从
127.0.0.1登录;生产环境 root 密码由密码管理器托管,不存于脚本或配置文件中 - 用
GRANT代替手写 INSERT/UPDATE:GRANT SELECT ON mydb.* TO 'appuser'@'10.0.1.%';—— 这样 MySQL 自动校验语法与逻辑,避免字段填错导致权限混乱 - 设置
read_only=ON对整个实例生效(需 SUPER 权限才能临时关闭),但它不保护mysql库 —— 所以必须配合super_read_only=ON(MySQL 5.7.20+ / 8.0),该参数才真正阻止对系统表的写入(包括mysql.user)
audit_log 插件必须开,否则等于没防
没有审计日志,你永远不知道是谁、什么时候、用什么语句改了 mysql.user。MySQL 官方 audit_log 插件(企业版)或社区替代方案如 mysql-audit(McAfee)能记录所有 DML/DCL 操作。
- 开启后,关键事件会记入日志:比如
UPDATE mysql.user SET authentication_string='xxx' WHERE User='root' - 注意:插件本身需要
INSTALL PLUGIN权限,且一旦启用,日志写入失败会导致部分连接拒绝(取决于audit_log_policy配置) - 日志路径默认在
/var/lib/mysql/audit.log,务必定期轮转并同步到独立日志服务器,避免被删库者顺手清空 - 别依赖慢查询日志——它不记录权限相关语句,
UPDATE mysql.user再快也不会进slow_query_log
复杂点在于:super_read_only 和 audit_log 都需要重启或动态加载,且部分云厂商 RDS 默认禁用插件安装。真要防住,得提前在部署阶段就固化策略,而不是等出事了再补。











