mysql用户权限中使用wildcard字符安全吗_mysql权限匹配规则

4次阅读

不安全,且极易误授权限;mysql将未转义的下划线_视为单字符通配符,导致grant语句意外匹配多个数据库,必须用反斜杠转义并以反引号包裹库名。

mysql用户权限中使用wildcard字符安全吗_mysql权限匹配规则

MySQL库名里用下划线_会被当通配符匹配

直接说结论:**不安全,且极易误授权限**。MySQL在解析数据库名时,会把未转义的_当作“单字符通配符”,和LIKE语句里的行为一致——这不是bug,是设计如此,但绝大多数人不知道。

比如你执行:
GRANT select ON `db_1`.* TO 'appuser'@'%';
你以为只给了db_1库的权限,实际上db01dba1db-1db?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_proddb_test的通配授权意外覆盖时,往往已经晚了。

text=ZqhQzanResources