revoke system_variables_admin后set global仍可执行,是因为未执行flush privileges刷新权限缓存,或用户仍持有session_variables_admin、connection_admin或super等高危权限;权限撤销仅阻止后续修改,不回滚已生效的变量值。

REVOKE SYSTEM_VARIABLES_ADMIN 后 SET GLOBAL 还能执行?先确认权限是否真被撤销了
直接结论:REVOKE SYSTEM_VARIABLES_ADMIN ON *.* FROM 'user'@'host' 本身不会报错,但不等于权限已失效——必须搭配 FLUSH PRIVILEGES 才会真正生效。MySQL 8.0+ 的动态权限是运行时加载的,不刷新就还是缓存里的旧状态。
常见错误现象:REVOKE 执行完立刻测试 SET GLOBAL sort_buffer_size = 4194304 仍成功,误以为权限没撤掉。其实是没刷缓存,或用户还持有其他高危权限。
- 执行后务必加一句:
FLUSH PRIVILEGES - 验证是否生效:
SHOW GRANTS FOR 'user'@'host'中不应再出现SYSTEM_VARIABLES_ADMIN - 注意大小写:权限名必须全大写、拼写完全一致,
system_variables_admin或System_Variables_Admin都无效
只撤 SYSTEM_VARIABLES_ADMIN 不够,还得查 SESSION_VARIABLES_ADMIN 和 CONNECTION_ADMIN
MySQL 8.0+ 把变量控制权拆开了:SYSTEM_VARIABLES_ADMIN 只管 SET GLOBAL,但 SET PERSIST 属于 SESSION_VARIABLES_ADMIN,而 CONNECTION_ADMIN 是个“全能钥匙”,隐式包含所有变量修改能力。
典型绕过场景:用户被撤了 SYSTEM_VARIABLES_ADMIN,但仍有 SESSION_VARIABLES_ADMIN,就能用 SET PERSIST sort_buffer_size = 1048576 持久化改全局变量,效果等同于 SET GLOBAL + 写入配置文件。
- 安全做法是三者一起撤:
REVOKE SYSTEM_VARIABLES_ADMIN, SESSION_VARIABLES_ADMIN, CONNECTION_ADMIN ON *.* FROM 'user'@'host' - 别漏掉
CONNECTION_ADMIN:它常被忽略,但权限位最高,连CREATE USER和SHUTDOWN都含在内 - 检查是否来自角色:
SHOW GRANTS FOR 'user'@'host' USING 'role_name',角色里也可能带这些权限
撤销后 SET GLOBAL 还能跑?不是权限没撤,是变量值早被改过了
权限回收只拦后续操作,不回滚历史变更。比如某人之前用 SET GLOBAL max_connections = 1000 改过值,你撤完权限,SELECT @@global.max_connections 还是 1000 —— 这不是漏洞,是设计如此。
真正“清理”需要重启 mysqld,并确保 my.cnf 里没手动覆盖该变量。否则下次启动还会加载上次 SET 的值(因为 SET GLOBAL 的结果只存在内存,但 SET PERSIST 会写进 mysqld-auto.cnf)。
- 查哪些变量被手动改过:
SELECT VARIABLE_NAME, VARIABLE_VALUE FROM performance_schema.global_variables WHERE VARIABLE_VALUE != (SELECT VARIABLE_VALUE FROM performance_schema.global_variables WHERE VARIABLE_NAME = CONCAT('default_', VARIABLE_NAME))(需结合默认值比对) - 删掉
/var/lib/mysql/mysqld-auto.cnf里对应条目,或清空整个文件(重启前备份) - 重启后验证:
SELECT @@global.max_connections应返回编译默认值(如 151),而非上次 SET 的值
为什么 SHOW GRANTS 看不到 SUPER,却还能 SET GLOBAL?
SUPER 在 MySQL 8.0.12+ 已废弃,只是兼容性别名。新账号默认不带 SUPER,而是直接分配细粒度权限。所以 REVOKE SUPER ON *.* FROM 'u'@'%' 可能只报 warning,实际什么都没撤掉。
更麻烦的是旧账号:升级前创建的用户可能仍保留 SUPER 权限位,且该权限未被自动映射为新权限,导致它和 SYSTEM_VARIABLES_ADMIN 并存——你只撤新权限,老 SUPER 还在起作用。
- 检查真实权限位:
SELECT User, Host, Super_priv FROM mysql.user WHERE User = 'user' AND Host = 'host' - 若
Super_priv = 'Y',必须显式撤:REVOKE SUPER ON *.* FROM 'user'@'host' - 撤完再
FLUSH PRIVILEGES,否则Super_priv字段不变,权限照常生效
权限回收最易被忽略的一点:动态权限不继承、不传播、不自动刷新。每个 REVOKE 都得配 FLUSH PRIVILEGES,每个高危权限都得单独查、单独撤,不能指望“一揽子”操作。尤其当用户通过角色获得权限时,光撤用户本身没用,角色也得同步处理。











