如何防御针对数据库元数据的SQL注入攻击?

风婷吖_5241

风婷吖_5241

2026-07-03

996人浏览

原创

preparedstatement的?占位符仅适用于值,不能用于表名、列名等sql结构部分,动态拼接这些元数据必须通过白名单校验,否则易导致元数据注入;mybatis中${}语法同理危险,应严格限制为预定义合法值。

如何防御针对数据库元数据的sql注入攻击?

直接拼接表名或列名时,PreparedStatement完全失效

很多人误以为只要用了 PreparedStatement 就万事大吉,但它的参数占位符 ? 只能用于**值(value)位置**,不能用于表名、列名、排序字段、GROUP BY 子句等结构部分。一旦你写成 "SELECT * FROM ? WHERE id = ?",运行时会直接抛出 SQLSyntaxErrorException 或类似错误——数据库根本不允许参数化对象名。

这意味着:如果业务需要动态指定表名(如分表查询)、列名(如动态字段导出)、或 ORDER BY 字段(如前端传参控制排序),你就必须手动拼接这部分 SQL,而这正是元数据注入的高危入口。

  • 常见错误写法:"SELECT " + columnName + " FROM users WHERE id = ?"
  • 攻击者输入 columnName = "name, password, (SELECT @@version)",可能触发子查询泄露数据库版本
  • 更危险的是:tableName = "users UNION SELECT 1,2,3 FROM mysql.user -- ",直接拖库

用白名单严格限制可拼接的元数据项

对动态表名、列名、排序字段等,不能靠“过滤单引号”或“转义”,而必须建立硬性白名单。这是唯一可靠的方式。

例如,支持按字段排序的接口,后端不应接受任意字符串,而应只允许预设的合法字段:

  • 定义白名单:Set<string> allowedSortFields = Set.of("id", "created_at", "status");</string>
  • 校验逻辑:if (!allowedSortFields.contains(sortField)) throw new IllegalArgumentException("Invalid sort field");
  • 拼接前强制转换为白名单内值:"ORDER BY " + sortField —— 此时 sortField 已是可信值,无需再过滤

同理,分表场景下,表后缀(如 user_2024、user_2025)也应从预生成的分表列表中匹配,而不是直接拼接用户传入的年份字符串。

避免在 SQL 中执行元数据查询(如 INFORMATION_SCHEMA)

有些业务逻辑会动态查 INFORMATION_SCHEMA.COLUMNS 或 SHOW COLUMNS FROM ... 来获取字段列表再拼 SQL,这本身就在引入元数据注入风险——因为表名仍是用户可控的。

Google Flow
Google Flow

一款AI视频创作工具,主要用于Google 推出的 AI 影片制作工具,适合需要提升相关任务效率的用户。

下载

典型错误:

  • "SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = '" + tableName + "'"
  • 攻击者传 tableName = "users' UNION SELECT table_name FROM INFORMATION_SCHEMA.TABLES -- "

正确做法是:元数据信息应预先加载并缓存(如启动时扫描所有业务表结构),运行时只从内存白名单中取值,绝不将用户输入带入任何 INFORMATION_SCHEMA 查询。

MyBatis 中 ${} 是元数据注入的雷区

MyBatis 的 #{} 是安全的(对应 PreparedStatement 参数),但 ${} 是纯字符串替换,不做任何转义,等价于直接拼接——它常被用来动态插入表名或字段名,也正是最常被滥用的地方。

  • 危险写法:SELECT * FROM ${tableName} WHERE id = #{id}
  • 安全替代方案:用 <bind></bind> + 白名单校验,或改用 <choose></choose> 分支明确枚举所有合法表名
  • 更稳妥的做法:把动态表名/列名逻辑提到 Java 层做白名单判断,SQL 中只用 #{} 绑定值

注意:即使加了 mysql_real_escape_string 类似的转义函数,也无法防御 ${} 注入,因为转义只对值有效,对已解析的 SQL 结构无效。

真正难防的不是“怎么写 SQL”,而是“哪些地方不得不动态拼接结构”。元数据注入的破绽往往藏在看似无害的排序、分页、导出、多租户路由等逻辑里——这些地方没有 ? 可用,只能靠设计阶段就划清白名单边界。

相关专题

更多
数据库三范式
数据库三范式

数据库三范式是一种设计规范,用于规范化关系型数据库中的数据结构,它通过消除冗余数据、提高数据库性能和数据一致性,提供了一种有效的数据库设计方法。本专题提供数据库三范式相关的文章、下载和课程。

2023.06.29

2505

3

如何删除数据库
如何删除数据库

删除数据库是指在MySQL中完全移除一个数据库及其所包含的所有数据和结构,作用包括:1、释放存储空间;2、确保数据的安全性;3、提高数据库的整体性能,加速查询和操作的执行速度。尽管删除数据库具有一些好处,但在执行任何删除操作之前,务必谨慎操作,并备份重要的数据。删除数据库将永久性地删除所有相关数据和结构,无法回滚。

2023.08.14

3781

10

vb怎么连接数据库
vb怎么连接数据库

在VB中,连接数据库通常使用ADO(ActiveX 数据对象)或 DAO(Data Access Objects)这两个技术来实现:1、引入ADO库;2、创建ADO连接对象;3、配置连接字符串;4、打开连接;5、执行SQL语句;6、处理查询结果;7、关闭连接即可。

2023.08.31

2731

3

MySQL恢复数据库
MySQL恢复数据库

MySQL恢复数据库的方法有使用物理备份恢复、使用逻辑备份恢复、使用二进制日志恢复和使用数据库复制进行恢复等。本专题为大家提供MySQL数据库相关的文章、下载、课程内容,供大家免费下载体验。

2023.09.05

907

5

vb中怎么连接access数据库
vb中怎么连接access数据库

vb中连接access数据库的步骤包括引用必要的命名空间、创建连接字符串、创建连接对象、打开连接、执行SQL语句和关闭连接。本专题为大家提供连接access数据库相关的文章、下载、课程内容,供大家免费下载体验。

2023.10.09

2427

5

数据库对象名无效怎么解决
数据库对象名无效怎么解决

数据库对象名无效解决办法:1、检查使用的对象名是否正确,确保没有拼写错误;2、检查数据库中是否已存在具有相同名称的对象,如果是,请更改对象名为一个不同的名称,然后重新创建;3、确保在连接数据库时使用了正确的用户名、密码和数据库名称;4、尝试重启数据库服务,然后再次尝试创建或使用对象;5、尝试更新驱动程序,然后再次尝试创建或使用对象。

2023.10.16

2447

4

vb连接access数据库的方法
vb连接access数据库的方法

vb连接access数据库方法:1、使用ADO连接,首先导入System.Data.OleDb模块,然后定义一个连接字符串,接着创建一个OleDbConnection对象并使用Open() 方法打开连接;2、使用DAO连接,首先导入 Microsoft.Jet.OLEDB模块,然后定义一个连接字符串,接着创建一个JetConnection对象并使用Open()方法打开连接即可。

2023.10.16

2813

3

vb连接数据库的方法
vb连接数据库的方法

vb连接数据库的方法有使用ADO对象库、使用OLEDB数据提供程序、使用ODBC数据源等。详细介绍:1、使用ADO对象库方法,ADO是一种用于访问数据库的COM组件,可以通过ADO连接数据库并执行SQL语句。可以使用ADODB.Connection对象来建立与数据库的连接,然后使用ADODB.Recordset对象来执行查询和操作数据;2、使用OLEDB数据提供程序方法等等。

2023.10.19

2241

3

mdf文件导入数据库的方法
mdf文件导入数据库的方法

mdf文件导入数据库的方法有使用SQL Server Management Studio、使用SQL Server命令行工具、使用SQL Server Integration Services等方法。想了解更多mdf文件导入数据库的方法的相关内容,可以阅读本专题下面的文章。

2023.10.23

2373

5

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
PHP开发基础之数据库篇(PDO)
PHP开发基础之数据库篇(PDO)

共10课时 | 2.3万人学习