GRANT语句中反引号内的_和%会被当作LIKE通配符,导致意外匹配多个数据库;正确做法是用db\_1形式转义下划线,并确保REVOKE语句与GRANT完全一致。

GRANT 语句里数据库名含 _ 或 % 会意外匹配多个库
MySQL 在解析 GRANT 语句时,对反引号包裹的数据库名中的 _ 和 % 默认启用 LIKE 模式匹配,不是字面量。比如执行 GRANT SELECT ON `db_1`.* TO 'u'@'%';,_ 会被当作“任意单字符”,实际匹配 db01、dba1、db-1 等所有第二位是任意字符、末位是 1 的库名。权限范围可能扩大几十倍,且无任何报错提示。
常见误操作包括:
- 写
GRANT ALL ON app_db.*却没加反引号,而库名实际是app_db(含下划线),结果被当成模式匹配 - 用
`log_%`授权时没确认INFORMATION_SCHEMA.SCHEMATA中真实存在的库名,导致匹配到log_backup、log_test_v2等非预期库 - 多处使用
_,如`data_202_`,每个_都独立匹配一个字符,组合爆炸风险极高
GRANT 中通配符必须用反引号 + 显式转义
要让下划线 _ 当作普通字符,必须在 SQL 字符串中双写反斜杠:_,且整个数据库名需用反引号包裹。例如授权给字面名为 db_1 的库,正确写法是:
GRANT SELECT ON `db_1`.* TO 'u'@'192.168.5.100';
注意:_ 是 MySQL 解析器要求的转义形式,不是 Shell 或客户端转义;执行后务必用 SHOW GRANTS FOR 'u'@'192.168.5.100'; 核对输出中是否显示为 `db_1` —— 如果显示为 `db_1`(无反斜杠),说明转义失败,权限仍在扩散。
其他要点:
-
%同样需包裹在反引号中才生效,dp%.*是语法错误,`dp%`.*才是合法通配模式 - 通配符只对数据库名有效,表名部分不支持
%或_模式匹配(除非用mysql.tables_priv表手动配置) - 如果库名不含特殊字符,也建议统一加反引号,避免未来重命名引入下划线后权限逻辑突变
REVOKE 必须与原始 GRANT 完全一致,否则静默失败
撤销权限时,MySQL 要求 REVOKE 语句的对象名、主机名、转义方式必须和当初 GRANT 时**逐字符相同**。例如:
授权用了 GRANT SELECT ON `db_1`.* TO 'u'@'%';,那么撤销必须写:
REVOKE SELECT ON `db_1`.* FROM 'u'@'%';
哪怕只漏掉一个反斜杠、或把反引号换成单引号、或主机名写成 'u'@'localhost',MySQL 都不会报错,也不会撤销——它只是找不到完全匹配的权限记录,然后默默跳过。
实操建议:
- 执行
SHOW GRANTS FOR 'u'@'%';,直接复制输出中的整行权限定义,再把GRANT替换为REVOKE - 撤销后立刻再执行一次
SHOW GRANTS,确认对应行已消失 - 禁止在生产环境凭记忆手写
REVOKE,尤其涉及带_的库名
检查现有通配符权限并收敛的最小动作集
别猜,先查。以下四条命令能快速定位风险点:
SELECT User, Host FROM mysql.user WHERE Host = '%';
SELECT User, Host, Db FROM mysql.db WHERE Db LIKE '%_%' OR Db LIKE '%%%' ESCAPE '';
SELECT SCHEMA_NAME FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME REGEXP '^db._1$';
SHOW GRANTS FOR 'u'@'%';
关键判断逻辑:
- 只要
mysql.user中存在Host = '%'的非 DBA 账号,就应立即收敛到具体 IP 段 -
mysql.db表中出现Db值含未转义_或%,基本等于已扩散权限,需结合SCHEMA_NAME查询结果人工确认影响范围 -
SHOW GRANTS输出中若存在ON `xxx%`.*或ON `xxx_`.*且未转义,说明该授权正在隐式扩大边界
真正危险的不是你写了通配符,而是你写了却不知道它正在匹配什么。每次授权前,先跑一遍 SELECT ... FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME LIKE 'pattern';,眼见为实。


















