grant all on . 会直接授予 super 和 file 权限,因其隐式包含 super、file、grant option 等高危权限,导致业务账号获得数据库 root 级控制能力,且无法通过常规 revoke 撤回。

GRANT ALL ON *.* 会直接授予 SUPER 和 FILE 权限
MySQL 的 GRANT ALL PRIVILEGES ON *.* 不是“所有普通操作权限”的集合,而是隐式包含 SUPER、FILE、GRANT OPTION 等高危权限。这些权限一旦落入业务账号之手,等于开放了数据库层面的 root shell:
-
SUPER可绕过只读模式、kill 任意连接、修改sql_mode或禁用审计日志 -
FILE允许通过SELECT ... INTO OUTFILE导出/etc/shadow,或用LOAD DATA INFILE注入恶意配置 -
GRANT OPTION让用户能把自己拥有的权限再转授他人,形成不可控的权限扩散链
真实案例中,一个被钓鱼的 app_user 拥有 ALL ON *.*,10 分钟内就导出了全部加密凭证并外传。
GRANT ALL 不等于“可控的全权限”,它跳过最小权限校验
开发常误以为“先给 ALL,再慢慢收”是稳妥做法,但 MySQL 权限模型不支持“叠加撤销”——REVOKE INSERT ON mydb.* FROM 'u'@'h' 不会撤回 ALL ON *.* 中已隐含的 INSERT 权限,除非先 REVOKE ALL PRIVILEGES ON *.* 再重授。
- 新用户创建后应先确认
SHOW GRANTS FOR 'u'@'h'返回的是GRANT USAGE ON *.*(空权限),而非默认带权 - 业务账号永远不要用
ON *.*,哪怕监控账号也要限定到performance_schema的具体视图 - 表级授权必须写死库名和表名,例如
GRANT SELECT ON shop.orders TO 'reporter'@'10.20.30.%'
host='%' 不是“方便”,而是放弃网络层第一道防线
GRANT ... TO 'user'@'%' 看似省事,实则让权限脱离 IP 控制:DNS 污染、防火墙策略失效、云主机安全组漏配时,该账号可被公网直连。更隐蔽的问题是:'%' 不匹配 localhost(Unix socket 连接走的是 'localhost'),导致本地调试反而连不上。
- 生产环境必须用 CIDR 表达 host,如
'app'@'10.20.0.0/16' - 若需跨网段访问,应在 iptables 或云安全组做二次限制,不能只依赖 MySQL 的 host 字段
- 删除默认匿名用户:
DROP USER ''@'localhost'; DROP USER ''@'%';,它们常被忽略但可能被利用
SHOW GRANTS 看不见真实权限,风险藏在角色和动态权限里
执行 SHOW GRANTS FOR 'u'@'h' 只显示直接授予的权限,完全不体现角色继承、PROXY 用户、或 MySQL 8.0+ 的动态权限(如 BACKUP_ADMIN)。一个看似只有 SELECT 的账号,可能通过 GRANT 'reader_role' TO 'u'@'h' 间接拿到 ALL ON *.*。
- 查完整权限链要用:
SELECT * FROM role_edges WHERE TO_HOST = 'u'@'h',再关联role_table_privs - 检查高危权限别只盯
GRANT OPTION,运行:SELECT user, host, Super_priv, File_priv FROM mysql.user WHERE Super_priv='Y' OR File_priv='Y' - 云数据库(如 RDS)通常屏蔽
mysql.user直接查询,此时只能依赖控制台审计日志,无法靠 SQL 自查
最危险的不是权限设错了,而是你根本不知道它已经存在——尤其当角色、代理用户、动态权限混用时,GRANT ALL 的影响早已超出语句字面范围。











