从库权限加固需四重防护:启用super_read_only=ON、回收业务账号mysql.*权限、配置replicate_ignore_db=mysql、开启审计日志。仅设read_only=ON无效,因它不限制系统表写入和FLUSH PRIVILEGES。

从库权限不能靠“只读”开关防住,业务账号只要拥有mysql.*写权限,就能在从库上INSERT/UPDATE系统表,再执行FLUSH PRIVILEGES让恶意权限立即生效——这是主从环境下最隐蔽的越权入口。
为什么从库的read_only=ON对权限篡改完全无效
很多人以为给从库设了read_only=ON就万事大吉,其实它只限制普通DML(如INSERT/UPDATE/DELETE业务表),但对mysql系统库完全不设防。只要账号有UPDATE权限写mysql.user或mysql.db,照样能:
- 执行
UPDATE mysql.user SET authentication_string = ? WHERE User = 'app',悄悄替换密码哈希 - 执行
INSERT INTO mysql.db VALUES ('%', 'myapp', 'app_user', 'Y', 'Y', 'Y', 'Y', 'N', 'N', 'N', 'N', 'N', 'N'),给自己开全库权限 - 再执行
FLUSH PRIVILEGES,新权限立刻加载进内存——而这个操作在从库上默认是允许的
必须显式禁用从库上的FLUSH PRIVILEGES和系统表写入
MySQL 5.7.20+ / 8.0 支持super_read_only=ON,这才是真正能拦住权限篡改的关键开关。它比read_only更严格:
-
read_only=ON:阻止普通用户写业务表,但允许SUPER权限用户操作系统表 -
super_read_only=ON:连SUPER用户也无法写mysql.*、无法执行FLUSH PRIVILEGES、无法SET GLOBAL - 启用后,只有重启mysqld才能临时关闭(且需启动参数
--super-read-only=OFF)
执行以下命令确认并开启:
SET PERSIST super_read_only = ON; FLUSH PRIVILEGES;
验证是否生效:SELECT @@super_read_only; 返回 1 才算真正锁死。
业务账号在从库上连mysql库都该被拒绝
即使开了super_read_only,如果业务账号仍能连上mysql库,就可能触发权限检查绕过或留下审计盲区。必须彻底切断访问路径:
- 执行
REVOKE ALL PRIVILEGES ON mysql.* FROM 'app_user'@'%';(注意host要匹配实际连接地址) - 确认
SHOW GRANTS FOR 'app_user'@'%';输出里已无任何mysql.*相关权限 - 检查是否有角色继承了系统库权限:
SELECT * FROM information_schema.role_table_grants WHERE grantee = "'app_user'@'%'" AND table_schema = 'mysql'; - 如果应用真需要查从库状态(如复制延迟),单独建一个只读监控账号:
CREATE USER 'repl_monitor'@'10.0.2.%' IDENTIFIED BY 'xxx'; GRANT REPLICATION CLIENT, PROCESS ON *.* TO 'repl_monitor'@'10.0.2.%';
从库权限加固后最容易被忽略的点
很多团队开了super_read_only就以为高枕无忧,但漏掉了两个关键事实:
- 所有权限变更(包括
REVOKE和SET PERSIST)必须搭配FLUSH PRIVILEGES才对当前会话生效;否则旧权限缓存还在,SHOW GRANTS看到的也不是真实状态 - 从库如果启用了
replicate_wild_ignore_table或replicate_ignore_db,而你又没把mysql库加进去,那主库上对mysql.user的修改仍会同步过来——所以务必确保replicate_ignore_db = mysql已配置并生效
真正的防线不是某一个开关,而是super_read_only + mysql.*权限回收 + 复制过滤 + 审计日志四者缺一不可。少一个,攻击者就可能从从库反向打穿整个权限体系。


















