mysql的show databases权限仅控制执行该命令时显示哪些数据库,不控制连接能力;设为n后只显示用户有权限的库,information_schema等系统库始终可见。
mysql 中 show databases 权限到底控制什么
它不控制“能否连接数据库”,而是控制“执行 show databases 命令时能看到哪些库”。默认情况下,只要用户能登录,show databases 就会列出所有数据库名——哪怕他对其中 99% 的库完全没访问权限。这是 mysql 的历史设计,不是 bug。
真正起作用的是全局 SHOW DATABASES 权限(属于 mysql.user 表的 show_db_priv 字段),但它的行为很反直觉:设为 N 后,用户执行 SHOW DATABASES 时只会看到自己有权限的库(基于 mysql.db 或 mysql.tables_priv 等授权表),而不是直接报错或空结果。
- 如果用户对某个库没有任何权限(比如没被
GRANT过任何操作),那个库就不会出现在SHOW DATABASES结果里 - 这个过滤只在执行
SHOW DATABASES时生效,不影响USE db_name或其他语句——如果用户碰巧知道库名,仍可能尝试切换并触发权限拒绝 -
information_schema和performance_schema总是可见(除非用--skip-show-databases启动 mysqld,但那是全局禁用)
如何让某用户只能看到自己被授权的库
核心动作是两步:关掉他的全局 SHOW DATABASES 权限 + 显式授予目标库的权限。不能只靠 GRANT 库级权限,必须显式回收全局权限。
以用户 'appuser'@'localhost' 为例:
REVOKE SHOW DATABASES ON *.* FROM 'appuser'@'localhost'; GRANT SELECT, INSERT ON `myapp_db`.* TO 'appuser'@'localhost'; FLUSH PRIVILEGES;
之后该用户执行 SHOW DATABASES,就只会看到 myapp_db(以及 information_schema 等系统库)。注意:REVOKE 必须明确写 ON *.*,否则无效;FLUSH PRIVILEGES 在大多数情况下可省略(只要用 GRANT/REVOKE 修改权限),但加了更稳妥。
- 别用
DROP USER再重建——旧权限残留风险高 - 如果用户已有其他全局权限(如
CREATE),REVOKE SHOW DATABASES不影响它们 - MySQL 8.0+ 的角色(ROLE)机制不改变这一逻辑,角色本身也需被
REVOKE SHOW DATABASES
常见错误现象和排查点
用户执行 SHOW DATABASES 仍看到全部库?大概率是没成功撤回权限,或者用了错误的 host 匹配。
- 检查实际生效的权限行:
SELECT Host,User,Show_db_priv FROM mysql.user WHERE User='appuser';—— 确认Show_db_priv是N - 注意
Host字段是否精确匹配,'appuser'@'%'和'appuser'@'localhost'是两个不同账户 - 如果用了代理或中间件(如 ProxySQL、MaxScale),可能缓存了旧权限,需刷新其权限缓存
- 某些客户端(如 phpMyAdmin)会自动执行
SHOW DATABASES并缓存结果,换终端或加mysql -N -s -e "SHOW DATABASES"直接验证
为什么不能彻底“禁止查看”而只能“过滤显示”
MySQL 没有“隐藏数据库元信息”的机制。只要用户能连上服务器,就能通过多种方式间接探测库存在,比如:
- 尝试
USE nonexist_db,错误提示里常含“Unknown database”而非“Access denied” - 查询
information_schema.SCHEMATA(如果没被禁用SELECT权限) - 利用存储过程或函数中的动态 SQL 绕过
所以生产环境若真要隔离敏感库名,得配合网络层控制(如只允许访问指定库的端口/路由)、应用层白名单,或把敏感库放到独立实例。单纯靠 SHOW DATABASES 权限只是第一道轻量过滤,别把它当安全边界。










