revoke system privilege system_user on . from 'u'@'h'是唯一合法语法,因system_user属动态权限,必须显式指定system privilege关键字,对象范围仅限.,不可加引号、不可合并撤销、需逐host处理且重连生效。

REVOKE SYSTEM PRIVILEGE 是唯一合法语法
直接写 REVOKE SYSTEM_USER ON *.* FROM 'u'@'h' 会报错 ERROR 3724 (HY000): Cannot revoke dynamic privilege 'SYSTEM_USER' using old-style REVOKE。MySQL 8.0.16+ 把 SYSTEM_USER、SYSTEM_VARIABLES_ADMIN 等全归为动态权限,必须显式带 SYSTEM PRIVILEGE 关键字。
正确写法只有一种:REVOKE SYSTEM PRIVILEGE SYSTEM_USER ON *.* FROM 'u'@'h'。注意三点:
-
SYSTEM_USER是权限名,不是角色名,不能加引号 - 对象范围固定为
*.*,不接受mydb.*或mydb.t1,否则语法错误 - 不能合并撤销多个动态权限,例如
REVOKE SYSTEM PRIVILEGE SYSTEM_USER, BACKUP_ADMIN ON *.* FROM ...—— MySQL 不支持逗号分隔
必须逐个检查并撤销所有 host 记录
权限按 User@Host 元组独立存储。'u'@'localhost' 和 'u'@'%' 是两个完全不同的账号,撤销其中一个对另一个毫无影响。
常见误操作是只撤了 'u'@'%',但用户实际用的是 'u'@'192.168.1.100' 登录,结果权限还在。
实操建议:
- 先查全量 host:
SELECT User, Host FROM mysql.user WHERE User = 'u'; - 对每个返回的
Host值,单独执行一次REVOKE SYSTEM PRIVILEGE ... ON *.* FROM 'u'@'host_value' - 特别注意通配符:如
'u'@'10.0.%'、'u'@'%.example.com'都需显式处理
撤销后必须重连才生效,且需验证是否真失效
动态权限在连接建立时加载快照,不会随 REVOKE 实时刷新。用户当前已建立的连接仍能执行 SHOW PROCESSLIST(如果撤的是 PROCESS)或 SET GLOBAL(如果撤的是 SYSTEM_VARIABLES_ADMIN),直到断开重连。
验证是否真正失效,不能只看 Query OK:
- 执行
SHOW GRANTS FOR 'u'@'h',输出里不应再出现对应权限行,例如GRANT SYSTEM_USER ON *.* TO 'u'@'h' - 更可靠方式是查系统表:
SELECT * FROM information_schema.role_table_grants WHERE grantee = "'u'@'h'" AND privilege_type = 'SYSTEM_USER',返回空才说明没残留 - 让应用重启或手动重连后,再用该账号测试敏感操作,例如执行
SHOW PROCESSLIST应报ERROR 1227 (42501)
别漏掉隐式高危权限:CONNECTION_ADMIN 和 SESSION_VARIABLES_ADMIN
只撤 SYSTEM_VARIABLES_ADMIN 不够。MySQL 8.0+ 把变量控制权拆开了:
-
SYSTEM_VARIABLES_ADMIN:只管SET GLOBAL -
SESSION_VARIABLES_ADMIN:管SET PERSIST和SET SESSION,它能持久化改全局变量,效果等同于SET GLOBAL + 写入配置文件 -
CONNECTION_ADMIN:最危险,隐式包含所有变量修改能力,还涵盖CREATE USER、SHUTDOWN等操作
典型绕过场景:你撤了 SYSTEM_VARIABLES_ADMIN,但用户仍有 CONNECTION_ADMIN,那他照样能执行 SET GLOBAL max_connections = 2000。
安全做法是三者一起撤:REVOKE SYSTEM PRIVILEGE SYSTEM_VARIABLES_ADMIN, SESSION_VARIABLES_ADMIN, CONNECTION_ADMIN ON *.* FROM 'u'@'h'。别跳过 CONNECTION_ADMIN —— 它常被忽略,却是最易被利用的入口。











