INFORMATION_SCHEMA 是 SQL 注入高危目标,因其无需额外权限、跨库可见、结构稳定,攻击者可直接枚举表与字段。MySQL 8.0+ 默认启用,PostgreSQL 的 pg_catalog 类似但更可控。防御需结合权限收缩、SQL 拦截、白名单校验及日志管控,仅靠参数化不足。

为什么 INFORMATION_SCHEMA 是 SQL 注入高危目标
攻击者一旦获得基础查询权限,INFORMATION_SCHEMA 就是第一站——它不需额外权限、跨库可见、结构稳定。用 SELECT * FROM INFORMATION_SCHEMA.TABLES 能直接列出所有表名,再配合 COLUMNS 表就能还原字段结构,为后续数据窃取铺路。MySQL 8.0+ 默认开启该库,PostgreSQL 的 pg_catalog 同理,但行为略有差异。
MySQL 中禁用或限制 INFORMATION_SCHEMA 访问的实操方式
MySQL 不允许直接 DROP DATABASE INFORMATION_SCHEMA 或 REVOKE 其权限,但可通过以下组合策略大幅削弱其利用价值:
- 创建应用专用账号时,**不授予
SELECT权限到INFORMATION_SCHEMA**:MySQL 实际上会忽略对该库的显式权限设置,但可配合下一条生效 - 启动 MySQL 时添加
--skip-show-databases参数,使SHOW DATABASES对非 super 用户失效(间接限制通过数据库名猜表) - 在应用层 SQL 拦截中,**正则匹配并拒绝含
INFORMATION_SCHEMA\.、information_schema\.或TABLES\s+WHERE等特征的查询**(注意大小写和空格变体) - 如用 MySQL 8.0+,启用角色机制,给应用账号分配最小角色,并确保该角色未被赋予
SELECT ON *.*—— 因为INFORMATION_SCHEMA的访问权隐式绑定在全局SELECT权限上
PostgreSQL 中更有效的 pg_catalog 限制手段
PostgreSQL 的 pg_catalog 行为比 MySQL 可控得多,且默认不向普通用户暴露完整元数据:
- 普通用户默认只能看到自己所属 schema 的对象,
pg_tables、pg_views等视图**自动过滤非授权对象**,无需额外配置 - 若需进一步收紧,可
REVOKE SELECT ON TABLE pg_tables FROM public,再按需GRANT SELECT给特定角色(注意:部分系统视图如pg_stat_activity需要pg_read_all_data角色,慎 revoke) - 禁止用户连接时使用
pg_database查询其他库存在性:在pg_hba.conf中限制database字段,或用连接池(如 pgbouncer)做路由隔离 - 避免在错误信息中泄露结构:确认
log_error_verbosity = default(而非verbose),且应用不将PgError原样返回前端
真正起效的防御必须落在应用层与参数化之外
靠数据库权限收缩只能增加攻击成本,不能替代编码规范。常见误区是以为“用了预处理语句就绝对安全”,但 INFORMATION_SCHEMA 攻击常出现在动态拼接的 WHERE table_name IN (...) 或 ORDER BY ? 场景中——这些位置**无法参数化**。
- 所有涉及表名、列名、排序字段、GROUP BY 字段的动态部分,必须走白名单校验,例如:
if not column_name in ['id', 'created_at', 'status']:直接拒掉 - 避免在应用日志里记录原始 SQL,尤其当它含
INFORMATION_SCHEMA关键字时——这类日志可能被低权限运维人员查看 - 对管理后台类系统,额外加一道中间层视图:只暴露必要元数据(如预定义的报表字段列表),而非直连系统表
最易被忽略的是:开发常把「没报错」等同于「没泄露」,但 SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='users' 这种语句即使返回 0,也已暴露了表是否存在。时间盲注和布尔盲注依然能从响应差异中提取结构信息。











