mysql不存在“权限溢出”概念,实际指高危权限(如super)被不当授予;需查mysql.user表的super_priv字段及8.0+的角色继承链,并通过kill query等操作验证真实权限,而非依赖show grants。

权限溢出不是MySQL的术语,先确认你真正想查的是什么
MySQL没有“权限溢出”这个错误或概念。你看到的报错大概率是 ERROR 1142 (42000): SELECT command denied 这类权限拒绝,或是误将“超级权限(SUPER)被滥用”说成“溢出”。真正的风险点在于:某些账号拥有远超业务所需的高危权限(比如 SUPER、FILE、GRANT OPTION),而你不知道是谁、什么时候、通过什么操作授予的。
直接查 mysql.user 表只能发现显式授权的超级用户
这是最快的一线筛查动作,但只对 MySQL 5.7 及之前版本完全可靠;8.0+ 必须补查角色链,否则漏人。
-
SUPER权限在mysql.user表里对应字段是Super_priv(注意大小写),执行:SELECT User, Host FROM mysql.user WHERE Super_priv = 'Y';
- 结果里包含
mysql.session、mysql.infoschema等系统账户,它们不能登录,不用管 - 刚执行过
GRANT SUPER ON *.* TO 'u'@'h'但没运行FLUSH PRIVILEGES?表里可能还是'N',而权限已生效——此时SHOW GRANTS不显示SUPER,但实际可用 - MySQL 8.0+ 用户即使
Super_priv = 'N',也可能通过角色获得SUPER,必须连查mysql.role_edges
查不到来源?因为 SHOW GRANTS 从不显示 SUPER
这是 MySQL 的硬性限制:无论用户是否真有 SUPER,SHOW GRANTS FOR 'u'@'h' 永远不会输出含 GRANT SUPER 的语句。别信它,它只展示显式 GRANT 语句,不解析角色继承、不读 mysql.user 快照。
- 验证是否真能用,比看语句更直接:用该账号登录后执行
KILL QUERY 1或SET GLOBAL max_connections = 1000 - 若成功,说明有
SUPER;失败再查角色链或直连授权记录 - 查角色继承分两步:
SELECT FROM_USER, FROM_HOST, TO_ROLE FROM mysql.role_edges;
对每个TO_ROLE执行SHOW GRANTS FOR 'role_name'@'%',再反查谁绑定了该角色
想定位“谁在什么时候授了权”?MySQL 默认不记,得靠外部手段
MySQL 自身不记录 GRANT / REVOKE 操作的历史。general_log 能捕获语句,但默认关闭且性能开销大;slow_query_log 和 error_log 完全无效——权限校验发生在执行前,根本不会进慢日志,错误日志也只记一行 Access denied for user,不含 SQL、库名、表名。
- 临时启用
general_log是唯一能拿到原始GRANT语句的办法:SET GLOBAL general_log = ON;
日志会记录所有到达 server 的语句(含失败的),配合时间戳和客户端 IP 分析 - 生产环境慎用:开启后 I/O 压力明显上升,建议只开几分钟,抓完立刻关
- 长期方案是部署审计插件(如
MySQL Enterprise Audit或开源替代Percona Audit Log),或通过数据库代理层(如ProxySQL)记录管理操作 - 如果已有备份,可对比
mysqldump mysql的历史快照,观察mysql.user表变化
真实权限问题往往卡在“你以为没给,其实给了”,或者“你查的表不对,权限藏在角色里”。别依赖 SHOW GRANTS,别指望错误日志告诉你谁授了权,更别把 Super_priv = 'N' 当成安全凭证——MySQL 8.0 的角色机制让权限溯源必须多走一步。











