查mysql.user表super_priv='y'仅是起点,mysql 8.0+必须联查role_edges、show grants for角色并确认set role已激活,否则漏检;show grants永不显示super,唯一验证方式是执行kill query或set global。

查mysql.user表里的Super_priv = 'Y'只是起点,不是终点
直接执行SELECT User, Host FROM mysql.user WHERE Super_priv = 'Y'能揪出显式授过SUPER的账号,但MySQL 8.0+中大量真实拥有该权限的账号根本不会出现在这个结果里——它们通过角色继承获得权限,在mysql.user里Super_priv字段仍是'N'。别把'N'当安全信号,它只代表“没直授”,不代表“没权限”。刚执行过GRANT SUPER ON *.* TO 'u'@'%'但还没FLUSH PRIVILEGES时,表里也可能还是'N',而权限早已生效。
必须连查mysql.role_edges和SHOW GRANTS FOR ROLE
角色继承链是隐藏超级权限的主通道,漏查就等于漏掉一半风险。分三步走:
- 先跑
SELECT FROM_USER, FROM_HOST, TO_ROLE FROM mysql.role_edges,拿到所有用户→角色的绑定关系 - 对每个
TO_ROLE,执行SHOW GRANTS FOR 'role_name'@'%'(注意主机名不能省,否则报ERROR 1141);若输出含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',当前会话才真正激活该权限;否则即使查到链路,也暂时不可用。
SHOW GRANTS FOR 'u'@'h'永远不显示SUPER,别信它
这是MySQL内部设计限制,不是你操作有误。SHOW GRANTS只输出显式执行过的GRANT语句,完全忽略角色继承、忽略直接改mysql.user表字段等非标准授权方式。所以:
- 没看到
GRANT SUPER ON *.* TO≠ 没有SUPER权限 - 看到
GRANT ALL PRIVILEGES ON *.* TO≠ 有SUPER(ALL不包含SUPER) - 唯一可靠验证方式是登录该账号后执行
KILL QUERY 1或SET GLOBAL max_connections = 100——成功了,就是真有
重点盯住Host = '%'且具备Grant_priv = 'Y'或Repl_slave_priv = 'Y'的组合
单纯Super_priv = 'Y'会把mysql.session这类系统账户也拉进来,它们不能登录,不算真实风险。真正要揪的是能从公网登录、还能转授或窃取数据的“活账号”:
-
Host = '%'或宽泛网段(如'10.%.%.%')的账号 - 用户名明显非DBA风格(如
'etl_user'、'bi_sync') - 同时具备
Grant_priv = 'Y'(可再授权)或Repl_slave_priv = 'Y'(可伪装从库拉全量数据) - 密码未定期更换、
account_locked = 'N'且长期无登录记录(需结合performance_schema或审计日志交叉判断)
最容易被忽略的是:这类账号可能没在mysql.user里显式标SUPER,却通过角色链+GRANT OPTION间接拿到了完整控制权——权限边界得靠实际操作去撞,不能只看静态字段。











