最快一线筛查是查mysql.user表super_priv='y',但mysql 8.0+需补查role_edges表和角色继承链,因角色授予的super权限在表中显示为'n'却实际生效。

直接查 mysql.user 表里的 Super_priv = 'Y' 是最快的一线筛查方式,但仅靠它会漏掉 MySQL 8.0+ 中通过角色继承获得 SUPER 权限的账号——这类账号在表里显示为 'N',却实际能执行 KILL、SET GLOBAL 等高危操作。
查 mysql.user 表的 Super_priv 字段(MySQL 5.7 及之前最准)
这个字段在旧版本中就是最终生效快照,不依赖角色、不走视图、不缓存中间态。执行:
SELECT User, Host FROM mysql.user WHERE Super_priv = 'Y';
注意以下几点:
-
Super_priv必须严格大写 S 和 P,写成super_priv或SUPER_PRIV会报错或查不到结果 - 必须用有
SELECT ON mysql.*权限的账号执行(如root或带SYSTEM_USER角色的用户) - 刚执行过
GRANT SUPER ON *.* TO 'u'@'h'但没运行FLUSH PRIVILEGES,表里可能还是'N',而权限已生效——此时SHOW GRANTS才是真实依据 -
mysql.session、mysql.infoschema这类系统账户也显示Super_priv = 'Y',但它们不能登录,不算可被滥用的“真实”超级账号
MySQL 8.0+ 必须连查 mysql.role_edges 补角色继承链
8.0 引入角色后,Super_priv 字段不再覆盖全部情况:用户可以没直连 'Y',却通过角色间接获得 SUPER。只查 mysql.user 一定漏人。
分两步走:
- 先查谁绑定了角色:
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,别信它
SHOW GRANTS FOR 'u'@'h' 输出里永远不出现 SUPER 字样,哪怕用户真有该权限。这是 MySQL 内部设计限制,不是你查错了。
它只展示显式 GRANT 语句,不解析角色继承、不读取 mysql.user 字段快照。所以:
- 没看到
GRANT SUPER ON *.* TO,不代表没权限——可能是角色继承,也可能是早期用INSERT INTO mysql.user直接改过字段 - 验证是否真能用:登录该账号后执行
KILL QUERY 1或SET GLOBAL max_connections = 1000,比看语句更直接 -
IS_ROLE_GRANTED()不反映 SUPER 实际生效状态,SHOW GRANTS ... USING role在 8.0.22+ 才支持且仍可能漏掉未激活角色
真正危险的不是 root,而是那些 Super_priv = 'N' 却绑了含 SUPER 的角色、又长期处于 SET ROLE 激活状态的账号——它们不会出现在基础查询里,却拥有完整服务级控制权。查完 mysql.user 后跳过 role_edges,等于只看了半张权限地图。











