MySQL 8.0+中仅查Super_priv='Y'会漏掉通过角色继承SUPER权限的高风险账户,必须联合mysql.role_edges、SHOW GRANTS FOR ROLE及反查用户角色绑定三步链路才能完整识别。

直接查 Super_priv = 'Y' 会漏掉大量真实高风险账户
MySQL 8.0+ 引入角色后,SELECT User, Host FROM mysql.user WHERE Super_priv = 'Y' 这条语句只返回直授 SUPER 权限的账号,完全忽略通过角色继承获得该权限的用户。比如一个叫 app_sync@'%' 的账号,本身 Super_priv 是 'N',但它被赋予了 dba_role,而该角色有 GRANT SUPER ON *.* TO —— 这个账号就具备完整 DBA 能力,但上述查询根本看不到。
必须联合 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,说明该角色带 DBA 级权限 - 再反查哪些用户被授了这个角色:
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 = 'dba_role';
这三步缺一不可。只做第一步或第二步,都会漏人。
SHOW GRANTS FOR 'u'@'h' 永远不显示 SUPER 权限
这不是你操作错了,是 MySQL 的设计限制:SHOW GRANTS 只输出显式执行过的 GRANT 语句,不反映角色链、不反映动态权限、也不反映通过 INSERT INTO mysql.user 直接改字段等非标准方式授予的权限。
所以:
- 没看到
GRANT SUPER ON *.* TO≠ 没有 SUPER 权限 - 看到
GRANT ALL PRIVILEGES ON *.* TO≠ 有 SUPER(ALL不包含SUPER) - 真正验证是否生效,只能登录后执行
KILL QUERY 1或SET GLOBAL max_connections = 100;成功了就是有
高风险非管理账户的判定不能只看权限字段
光筛选出有 Super_priv = 'Y' 或角色继承 SUPER 的账号还不够,还得过滤掉系统账号和低风险账号:
- 排除
mysql.session、mysql.infoschema这类无法登录的系统账户(account_locked = 'Y'或plugin = 'mysql_native_password'为'') - 重点关注
Host为'%'、'10.%'、'192.168.%'等宽泛网段的账号 - 用户名明显非 DBA 命名习惯(如
report_user、etl_worker、sync_admin) - 同时具备
Grant_priv = 'Y'或Repl_slave_priv = 'Y'—— 这两个权限组合起来,能伪造主从、提权、甚至读取任意服务器文件
最易被忽略的是:刚执行过 GRANT SUPER ON *.* TO 的账号,其权限在 mysql.user 表里不会立刻刷新,且角色绑定后必须用户主动执行 SET ROLE 才激活——这意味着审计脚本查到的“有权限”,不等于当前会话真能用。











