mysql 8.0.12+ 可直接执行 revoke select on information_schema.* from 'user'@'%';,该操作有效且自动生效;5.7 及更早版本不支持此语法,需通过严格限定业务库权限并禁用 show databases 等方式间接限制。

MySQL 8.0+ 能否直接 REVOKE SELECT ON INFORMATION_SCHEMA.*
可以,但仅限 MySQL 8.0.12 及以上版本。执行 REVOKE SELECT ON INFORMATION_SCHEMA.* FROM 'user'@'%' 是有效的,且会立即阻止该用户查询 INFORMATION_SCHEMA.TABLES、INFORMATION_SCHEMA.COLUMNS 等视图。
常见错误现象:
– 在 MySQL 5.7 或更早版本执行该语句会报错 ERROR 1044 (42000): Access denied for user,因为旧版本不支持对 INFORMATION_SCHEMA 显式授权/回收;
– 即使在 8.0+,若用户已有全局权限(如 SELECT ON *.*),REVOKE 必须完全匹配原 GRANT 范围,否则无效。
实操建议:
– 先确认版本:SELECT VERSION();
– 检查当前授权:SHOW GRANTS FOR 'user'@'%';
– 若存在 GRANT SELECT ON *.*,必须先 REVOKE SELECT ON *.* FROM 'user'@'%',再单独授业务库表权限;
– 执行 REVOKE SELECT ON INFORMATION_SCHEMA.* FROM 'user'@'%' 后无需 FLUSH PRIVILEGES(GRANT/REVOKE 自动生效)。
为什么 SHOW DATABASES 还能看到库名,即使没给 INFORMATION_SCHEMA 权限
因为 SHOW DATABASES 的可见性不依赖 INFORMATION_SCHEMA,而取决于用户是否拥有任意一个数据库的 USAGE 或其他权限。只要用户对某个库有 SELECT、INSERT 等权限,SHOW DATABASES 就会列出所有他“能访问”的库名——这是 MySQL 的默认行为。
真正屏蔽库名的方法只有一种:skip-show-databases。
实操建议:
– 在 my.cnf 的 [mysqld] 段落中添加:skip-show-databases;
– 重启 MySQL 服务(必须重启,动态 SET 不生效);
– 验证效果:用受限用户登录后执行 SHOW DATABASES,应返回空结果集;
– 注意:skip-show-databases 对 root 或拥有 SUPER 权限的用户无效,所以务必确保应用账号无 WITH GRANT OPTION 或 SUPER。
information_schema_stats_expiry = 0 是不是能防元数据探测
不能防,只能干扰。设 SET PERSIST information_schema_stats_expiry = 0 会让统计信息(如 TABLE_ROWS、索引基数)每次查询都实时计算,增加探测延迟和服务器负载,但不会隐藏表名、列名或结构本身。
它不影响 SELECT table_name FROM INFORMATION_SCHEMA.TABLES WHERE table_schema = 'mydb' 这类基础查询——只要用户有对应库的 SELECT 权限,这条语句依然能跑通(除非已 REVOKE 掉 INFORMATION_SCHEMA 权限)。
实操建议:
– 别把它当安全措施,只是辅助扰动手段;
– 优先级远低于:显式 REVOKE SELECT ON INFORMATION_SCHEMA.* + skip-show-databases + 限制业务库范围;
– 如果应用不需要统计信息(如报表系统不依赖 TABLE_ROWS),可设为 0;否则保持默认(如 86400 秒)更省资源。
低版本 MySQL(5.7)如何等效限制 INFORMATION_SCHEMA 访问
MySQL 5.7 不支持 REVOKE SELECT ON INFORMATION_SCHEMA.*,也不能靠权限回收实现,唯一有效路径是「切断元数据来源」:让用户对任何数据库都没有全局权限,且不授予 SHOW DATABASES 权限,并确保其只能访问明确指定的单个数据库。
关键点在于:MySQL 5.7 的 INFORMATION_SCHEMA 是按用户实际可访问的库动态生成的。如果用户只被授予 myapp_db.users 的 SELECT 权限,那 SELECT * FROM INFORMATION_SCHEMA.TABLES 返回的就只有 myapp_db 下的表——但这仍可能泄露表名。要彻底压制,必须配合应用层约束或代理层过滤。
实操建议:
– 创建用户时不用 GRANT ... ON *.*,严格限定到库表:GRANT SELECT ON myapp_db.users TO 'app'@'%';
– 显式回收可能残留的权限:REVOKE ALL PRIVILEGES ON *.* FROM 'app'@'%';
– 确保未授予 SHOW DATABASES、PROCESS、REPLICATION CLIENT 等辅助权限;
– 在应用连接串中加 database=myapp_db,并用 USE myapp_db 初始化,默认库缺失会降低跨库尝试成功率;
– 最终防线:在中间件(如 ProxySQL)或 ORM 层禁止含 INFORMATION_SCHEMA、mysql.、performance_schema. 的查询关键词。
INFORMATION_SCHEMA 不是普通数据库,它的内容是用户权限的镜像。想让它“空”,核心不是删它、锁它,而是让用户的权限范围足够窄——窄到连一张额外的表都够不到。这点在 MySQL 5.7 和 8.0 上逻辑一致,只是 8.0 多了道显式回收的保险栓。











