必须联合查询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 激活后,照样能执行 KILL QUERY 或 SET GLOBAL。
查 mysql.user 表的 Super_priv 字段(必须做,但不够)
这是所有版本都得跑的第一步,反映用户行级权限快照:
-
Super_priv大小写敏感:必须写成Super_priv,super_priv或SUPER_PRIV会报错或返回空 - 必须用
root或带SYSTEM_USER角色的账号执行;普通用户默认无SELECT ON mysql.*权限 -
Host值必须完全匹配:例如'localhost'≠'127.0.0.1'≠'%' - 刚执行过
GRANT SUPER ON *.* TO 'u'@'h'但没运行FLUSH PRIVILEGES,表中可能仍为'N',而权限已实际生效 - 系统账户如
mysql.session、mysql.infoschema也显示'Y',但它们不能登录,不算可被滥用的账号
补查 mysql.role_edges 和角色继承链(MySQL 8.0+ 必做)
只查 mysql.user 会漏掉通过角色间接获得 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 PRIVILEGES默认不包含SUPER,必须显式授予 - 没看到
GRANT SUPER ON *.* TO,不代表没权限——可能是角色继承,也可能是早期手动改过mysql.user表
最终验证只能靠实操:登录后执行高危命令
权限不是连接建立时就“写死”的状态,而是每次执行操作时实时校验:
- 对疑似账号,直接登录后试:
KILL QUERY 1;或SET GLOBAL max_connections = 1000; - 成功即生效;失败则说明当前会话无权限(可能是未
SET ROLE,或角色链未命中) - 批量检查时无法依赖
SHOW GRANTS,它每次只能查一个用户,且不支持跨角色链自动展开 - 删掉带 SUPER 的角色(
DROP ROLE 'xxx';)不会自动清理已授权用户——必须人工确认并执行REVOKE 'xxx' FROM 'u'@'h';
最易被忽略的点:角色继承路径查到了,SHOW GRANTS 也确认角色含 SUPER,但用户没执行过 SET ROLE —— 当前连接依然没有权限。这个激活步骤是硬性前提,不写脚本、不进自动化流程,就容易漏检。











