MySQL库名里用下划线_
会被当通配符匹配
直接说结论:**不安全,且极易误授权限**。MySQL在解析数据库名时,会把未转义的
_当作“单字符通配符”,和LIKE语句里的行为一致——这不是bug,是设计如此,但绝大多数人不知道。
比如你执行:
GRANT SELECT ON `db_1`.* TO 'appuser'@'%';
你以为只给了
db_1库的权限,实际上
db01、
dba1、
db-1、
db?1……所有第二个字符是任意单字符、结尾是
1的库,全都被匹配到了。 一个
_可能扩大权限至 30+ 个意外库(按常见命名字符集估算) 两个
_(如
db_1_2)→ 匹配数≈30×30=900,权限失控风险指数级上升 若这些库中混有测试库、备份库或含敏感字段的旧业务库,后果就是越权读取
正确写法:必须用反斜杠\
转义下划线
MySQL支持用
\对
_和
%做字面量转义,这是唯一可靠的方式。
正确授权示例:
GRANT SELECT ON `db\_1`.* TO 'appuser'@'%';
注意:反斜杠在SQL字符串中本身需被MySQL解析,所以命令行或脚本中要写成两个反斜杠
\(例如Shell中),但在MySQL客户端内直接执行,一个
\即可生效。 必须用反引号
`包裹库名,否则转义无效(无引号时
db\_1会被当成非法标识符报错) 不要依赖“我库名只用字母数字”来侥幸——只要命名规范允许
_,就存在被误匹配的可能 自动化部署脚本中务必检查所有
GRANT语句,grep
`.*_.*`+ 手动验证是否已转义
权限匹配顺序决定“谁说了算”,不是越具体越优先
MySQL权限验证是**从上到下、先到先得**,不是“最精确匹配胜出”。它依次检查:
mysql.user(全局)→
mysql.db(库级)→
mysql.tables_priv(表级)→
mysql.columns_priv(列级)。一旦某一层找到匹配项(哪怕只是
'%'@'%'这种宽泛主机),就立即停止向下查。 这意味着:如果用户同时有
SELECT ON *.*(全局)和
SELECT ON `db\_1`.*(库级),前者会拦截所有请求,后者完全不生效 撤销权限时,
REVOKE SELECT ON `db\_1`.*不会影响全局权限;必须显式
REVOKE SELECT ON *.*才能真正收权 用
SHOW GRANTS FOR 'user'@'host';看到的权限列表,是MySQL当前实际生效的组合结果,不是“所有授予过的权限”
生产环境建议:禁用通配符式授权,改用角色+白名单
靠人工盯住每个
_是否转义,长期来看不可靠。更稳妥的做法是绕过通配符问题本身。 用
CREATE ROLE(MySQL 8.0+)定义角色,如
app_reader,只授明确库名:
GRANT SELECT ON `db_1`.* TO app_reader;应用账户不直接受权,而是
GRANT app_reader TO 'appuser'@'%';,后续增删库只需调整角色,不碰用户 旧版本(5.7)可用脚本批量生成严格库名授权语句,避免手写;同时禁止在
GRANT中出现任何
_或
%字符 CI/CD流水线中加入SQL语法扫描,对含
GRANT.*ON.*`.*_.*`但无
\_的语句直接阻断发布
真正麻烦的从来不是记不住转义规则,而是权限条目散落在不同时间、不同人、不同脚本里——等发现
db_prod被
db_test的通配授权意外覆盖时,往往已经晚了。
