with grant option 极其危险,它允许被授权者转授自身所有权限(含后续获得的),导致权限失控扩散;mysql 不记录 grant 操作,回收不级联,唯一可靠检查方式是查询 mysql.user 表中 grant_priv = 'y'。

WITH GRANT OPTION 会绕过权限边界
它不是“允许执行 GRANT 语句”这么简单,而是让被授权者能把自己当前拥有的所有权限(包括后续别人再授给他的)原样转授出去。哪怕你只给了 SELECT ON mydb.users,加上 WITH GRANT OPTION 后,对方就能执行 GRANT SELECT ON mydb.users TO 'hacker'@'%'——而你根本没授权他管这个用户。
更危险的是:如果他后来被别人授予了 UPDATE ON mysql.user,他也能把这条权限再转出去,等于在你不知情时打开了系统表写入通道。
- MySQL 不区分“管理权”和“数据权”,
WITH GRANT OPTION一开,两类权限全可扩散 - 权限继承是隐式的,
SHOW GRANTS FOR 'user'@'host'只显示直接授予的,不显示他转授过谁 - 8.0+ 中角色继承权限后,若角色带
WITH ADMIN OPTION,扩散路径更难追踪
误用后几乎无法审计和回收
一旦发生误授权,MySQL 本身不记录“谁把权限给了谁”。mysql.role_edges 表只记录角色分配,不记录普通用户的 GRANT 操作;audit_log 默认关闭,且需额外配置才能捕获 GRANT 语句。
常见错误现象:Access denied; you need (at least one of) the GRANT OPTION privilege(s) 这类报错常让人误以为只是“缺权限”,其实背后可能是某账号已被悄悄赋予了 GRANT OPTION 并开始扩散。
- 查
mysql.user表中Grant_priv = 'Y'是唯一可靠方式,但很多人从不扫描 -
REVOKE GRANT OPTION ON *.* FROM 'user'@'host'可回收,但已转授出去的权限不会自动失效(MySQL 不做级联回收) - 如果误授发生在
ON *.*级别,对方甚至能GRANT SUPER ON *.* TO,彻底脱离管控
替代方案:用角色 + WITH ADMIN OPTION 控制授权范围
MySQL 8.0+ 的 WITH ADMIN OPTION 只作用于角色,且只能控制“谁能把这个角色再授给别人”,不能用于单条权限或全局对象。这才是可控分级管理的唯一正解。
比如你要让运维组长能管理备份权限,但不能碰用户库:
- 先建角色:
CREATE ROLE 'backup_admin' - 只授最小权限:
GRANT LOCK TABLES, SELECT ON *.* TO 'backup_admin' - 再把“授角色权”交给指定人:
GRANT 'backup_admin' TO 'ops_lead'@'10.0.1.%' WITH ADMIN OPTION - 此时
ops_lead只能执行GRANT 'backup_admin' TO ...,不能执行GRANT SELECT ON mysql.* TO ...
角色必须显式激活(SET ROLE 'backup_admin')或设为默认角色才生效,天然增加一层确认环节。
生产环境该禁用就禁用,别留侥幸
99% 的应用账号、中间件账号、监控账号完全不需要 WITH GRANT OPTION。它存在的唯一合理场景,是 DBA 团队内部极短期的临时委派,且必须配套操作日志和事后回收。
真正容易被忽略的点是:GRANT ALL PRIVILEGES ON *.* 会隐式包含 GRANT OPTION 和对 mysql 库的写权限——这比单独加 WITH GRANT OPTION 更危险,因为连系统表都能改。
检查命令要定期跑:SELECT User, Host FROM mysql.user WHERE Grant_priv = 'Y',发现非 DBA 账号有 Y,立刻 REVOKE GRANT OPTION ON *.* FROM。别等出事再查。











