禁用元数据访问需分层防控:mysql用skip-show-databases+限定库权限;postgresql撤回pg_catalog usage及关键视图select;sql server撤销view definition;应用层禁用运行时查元数据。

MySQL 中如何禁用 information_schema 查询
默认情况下,普通用户也能查 information_schema,这是 SQL 注入后获取表名、列名的主要跳板。不能靠“不拼接 SQL”就高枕无忧,得从权限层堵死。
实操上,直接回收对 information_schema 的 SELECT 权限无效(MySQL 8.0+ 允许显式授权但不生效),真正有效的是:限制用户只能访问指定数据库,并关闭元数据访问开关。
- 创建用户时用
CREATE USER 'app'@'%' IDENTIFIED BY 'pwd',**不要**加WITH GRANT OPTION - 只授具体库权限:
GRANT SELECT, INSERT, UPDATE ON myapp_db.* TO 'app'@'%',**不授**mysql或information_schema任何权限 - MySQL 8.0.29+ 可设系统变量:
SET PERSIST information_schema_stats_expiry = 0(降低统计信息缓存,间接增加探测成本,非根治但有干扰作用) - 关键一步:在
my.cnf中加skip-show-databases—— 这会让SHOW DATABASES对非 super 用户返回空,连库名都看不到
PostgreSQL 怎么关掉 pg_catalog 和 information_schema
PostgreSQL 的元数据更开放,默认所有用户都能查 pg_tables、pg_columns 等视图。不像 MySQL 那样能简单禁用,得靠撤销视图权限 + 撤销 schema 使用权。
注意:不能删或重命名 pg_catalog,那是核心 schema;也不能直接 REVOKE SELECT ON ALL TABLES IN SCHEMA pg_catalog,因为部分视图依赖 superuser 权限,普通 revoke 会报错。
- 先确保应用用户不是
pg_read_all_data角色成员(这个角色默认可读所有系统表) - 逐个撤回关键视图权限:
REVOKE SELECT ON pg_tables, pg_views, pg_columns FROM app_user - 撤回整个 schema 的使用权限:
REVOKE USAGE ON SCHEMA pg_catalog FROM app_user(这步让SELECT * FROM pg_catalog.pg_tables直接报permission denied for schema pg_catalog) - PostgreSQL 15+ 支持行级安全策略(RLS),可在
pg_tables上加策略,但实际中极少用——太重,且 RLS 不作用于元数据视图
SQL Server 里 sys.tables 查不到的真正原因
SQL Server 默认对普通用户隐藏系统视图内容,但只要用户有 VIEW DEFINITION 权限或属于 db_owner,就能看到真实表结构。泄露常发生在误授了这类权限,或者用了 sa 登录的应用连接字符串。
别信“没给 db_owner 就安全”,db_datareader 角色本身不暴露表名,但一旦应用执行了 SELECT * FROM sys.tables,SQL Server 会按当前用户权限动态过滤结果 —— 所以关键不是“能不能查”,而是“查出来多少”。
- 确认用户是否被显式授予
VIEW DEFINITION:SELECT * FROM sys.database_permissions WHERE grantee_principal_id = USER_ID('app_user') - 撤销它:
REVOKE VIEW DEFINITION ON DATABASE::MyDB TO app_user - 禁用
SELECT对sysschema 的隐式访问:在数据库属性 → 权限页中,取消勾选Grant下的VIEW ANY DEFINITION - 最稳妥的做法:应用连接字符串里用最小权限账号,且该账号只在必要时才被临时加进
db_datareader,不用时立刻移出
应用层配合:为什么 ORM 也得设 column_name 白名单
即使数据库权限收紧,如果应用代码里还用 SELECT * 或反射自动拼 INFORMATION_SCHEMA.COLUMNS 去生成字段列表,那等于把元数据查询逻辑写死了。攻击者只要触发任意一处 SQL 注入,就能绕过权限限制,靠应用自身行为把表结构吐出来。
这不是数据库配置能解决的问题,是代码习惯问题。尤其常见于动态查询构建、日志字段自动采集、低代码平台的元数据同步模块。
- 禁止运行时查
INFORMATION_SCHEMA构建 SQL —— 所有字段名必须硬编码或来自可信配置 - ORM 如 SQLAlchemy,避免用
inspect(engine).get_columns(...)做运行时判断;Django 的connection.introspection.table_names()同理 - 如果真要动态字段,走独立权限隔离的服务接口,而不是复用主应用 DB 连接
- 上线前用 SQL 解析工具扫一遍代码,grep
INFORMATION_SCHEMA、pg_tables、sys.columns这类关键词
元数据泄露从来不是单点问题。数据库权限只是最后一道门,门锁再牢,钥匙(应用代码)要是随便借人,照样白搭。










