必须联合查询mysql.user表Super_priv='Y'和mysql.role_edges角色继承链,并确认用户已执行SET ROLE激活,否则MySQL 8.0+下仅查Super_priv会漏检大量真实拥有SUPER权限的账号。

直接查 mysql.user 表里 Super_priv = 'Y' 的账号只是起点,MySQL 8.0+ 环境下必然漏人——真正拥有 SUPER 权限的账号,可能字段值是 'N',却通过角色继承 + SET ROLE 激活后完全等效。
查 mysql.user 表只能抓直授,不能信“N”代表没权限
执行这条语句是必须的第一步:
SELECT User, Host, Super_priv, Grant_priv, Repl_slave_priv FROM mysql.user WHERE Super_priv = 'Y' OR Grant_priv = 'Y' OR Repl_slave_priv = 'Y';
但要注意:
-
Super_priv大小写敏感,写成super_priv或SUPER_PRIV会查不到或报错 - 刚执行过
GRANT SUPER ON *.* TO 'u'@'h'但没运行FLUSH PRIVILEGES,表中仍为'N',而权限已实际生效 - 系统账户如
mysql.session、mysql.infoschema也显示'Y',但它们不能登录,不算真实风险点 -
Host值必须完全匹配:例如'localhost'≠'127.0.0.1'≠'%','%'的账号要优先标记为高危
MySQL 8.0+ 必须连查 mysql.role_edges 补角色链
只靠 mysql.user 查 Super_priv 在 8.0+ 下一定漏检。角色继承来的 SUPER 不写入用户行,且需显式激活才生效。
分三步走:
- 先查绑定关系:
SELECT FROM_USER, FROM_HOST, TO_ROLE FROM mysql.role_edges; - 对每个
TO_ROLE,执行SHOW GRANTS FOR 'role_name'@'%';(注意:角色名必须带主机名,否则报错);若输出含GRANT SUPER ON *.* TO,说明该角色链能提供SUPER - 再反查哪些用户绑定了该角色:
SELECT User, Host FROM mysql.user u JOIN mysql.role_edges r ON u.User = r.FROM_USER AND u.Host = r.FROM_HOST WHERE r.TO_ROLE = 'xxx';
关键前提:SET ROLE 'xxx'; 必须已在目标会话中执行过,否则即使查到角色链,当前连接也无 SUPER 权限。
SHOW GRANTS 永远不显示 SUPER,别拿它当依据
这是 MySQL 内部设计限制,不是你操作有误:
-
SHOW GRANTS FOR 'u'@'h'输出里永远不会出现SUPER关键字,哪怕用户真有该权限 - 它只展示显式执行过的
GRANT语句,不解析角色继承,也不读取mysql.user字段快照 - 看到
GRANT ALL PRIVILEGES ON *.* TO不代表有SUPER(MySQL 8.0+ 中ALL默认不含SUPER) - 没看到
GRANT SUPER ON *.* TO也不等于没权限——可能是角色继承,也可能是早期手动改过mysql.user表
最终验证只能靠实操:用疑似账号登录后,直接执行 KILL QUERY 1; 或 SET GLOBAL max_connections = 100;。成功了,就是真有。
高风险非管理账号的判定不能只看字段值
单纯筛选 Super_priv = 'Y' 会把系统账户也拉进来。真正要揪的是:
-
Host为'%'或网段宽泛(如'10.%.%.%')的账号 - 用户名明显非 DBA 习惯命名(如
'app_report'、'sync_user') - 同时具备
Grant_priv = 'Y'或Repl_slave_priv = 'Y'的组合(形成权限扩散链) -
account_locked = 'N'且长期无登录记录(需结合performance_schema或审计日志交叉判断)
最容易被忽略的一点:刚执行 GRANT SUPER ON *.* TO 'u'@'h' 但没运行 FLUSH PRIVILEGES,此时 mysql.user 表仍是 'N',而权限已生效——这种“半同步”状态下的账号,既不会出现在基础查询结果里,又确实能执行高危操作。


















