游乐游手机版
首页/数据库/文章详情

MyBatis中#{}和${}预防SQL注入效果对比

时间:2026-06-27 06:52
在MyBatis中,只有 {}能有效防御SQL注入,它通过PreparedStatement将参数作为数据处理;${}本质是字符串拼接,无防护机制,但在表名、列名等语法位置必须使用,此时需通过白名单、元信息校验等硬编码措施确保安全。

需要明确的是:在 MyBatis 框架中,真正能够有效防御 SQL 注入攻击的只有 #{} 写法。而 ${} 本质上是直接进行字符串拼接,并不提供任何防护能力——传入什么就原样拼接到 SQL 中,最终数据库接收到的是完整的 SQL 文本,而非参数化的查询。一旦攻击者发现某个字段未经严格校验,就可以利用 ' OR '1'='1; DROP TABLE user; -- 执行任意恶意语句。这绝非危言耸听。

MyBatis中#{}和${}哪个更能有效预防SQL注入风险?

#{ } 是唯一能有效防范 SQL 注入的写法

在 MyBatis 中,只有 #{} 能够实现真正的 SQL 注入防御,而 ${} 本身不附带任何安全机制——它只是纯粹地进行字符串拼接,参数值会被直接嵌入到 SQL 语句中。数据库接收到的不是参数占位符,而是完整的可执行 SQL 字符串。即便只遗漏一个字段的校验,攻击者也有可能利用 ' OR '1'='1; DROP TABLE user; -- 执行任意操作。这并非设计缺陷,而是其底层工作方式决定的。

为什么 #{ } 能够成功拦截恶意输入?

核心在于 #{} 会启动 JDBC 的 PreparedStatement 机制,该机制天生具备防注入特性:

  • MyBatis 先将 #{id} 替换为 ? 占位符,SQL 语句发送到数据库时结构已经固定,例如 SELECT * FROM user WHERE id = ?
  • 参数值通过 setLong(1, id)setString(1, value) 单独绑定,数据库仅将其视为数据值,不会作为语法解析
  • 即使传入 "1; DROP TABLE user; --",实际执行的仍是 WHERE id = '1; DROP TABLE user; --' —— 仅仅是一条无法查出结果的普通查询

可以想象,数据库接收到的是已经定型的 SQL 骨架,参数仅仅是被填充的数据值,无论怎样尝试都无法改变查询结构。

哪些场景不得不使用 ${ }?

需要说明的是,使用 ${} 并非出于偷懒,而是 JDBC 层面根本不允许用 #{} 的硬性场景:

  • 表名:SELECT * FROM ${tableName} —— 如果使用 #{tableName} 会添加单引号导致变成 FROM 'user',直接引发语法错误
  • 列名或排序字段:ORDER BY ${column} ${orderType} —— #{orderType} 会变成 ORDER BY name 'ASC',同样报错
  • GROUP BY、LIMIT 偏移量等无法添加引号的语法位置

这些场景使用 ${} 并非偷懒,而是别无选择。既然必须使用,就必须将安全措施落实到位。

使用 ${ } 时最容易被忽略的安全措施

很多人认为“加个正则校验”或“限制输入长度”就足够了,实际上还远远不够。真正稳妥的做法至少需要执行以下步骤:

  • 白名单必须硬编码:例如排序字段仅允许 Set.of("id", "name", "created_at"),前端传入 sort=user_name,后端映射为真实列名 "name"
  • 表名需要双重校验:先匹配正则 ^[a-zA-Z][a-zA-Z0-9_]{2,31}$,再查询数据库元信息确认该表确实存在
  • 绝对禁止用户直接传递字段名或表名:所有动态片段都应来自配置或枚举,而不是前端的原始输入

如果不执行这些步骤,${column} 与直接使用 Statement 没有本质区别,风险完全暴露。务必牢记:凡是能用 #{} 的地方绝不使用 ${},在必须使用 ${} 的场合,每增加一层校验就多一份安全保障。

来源:https://www.php.cn/faq/2693513.html
上一篇SQL递归视图处理层级组织架构数据 下一篇SQL中DISTINCT与COUNT统计不重复活跃用户的方法
本站内容用于信息整理与展示,如有侵权或内容问题请及时联系处理。

相关推荐

补充同频道和同主题内容,方便继续浏览更多相关内容。

同类最新

继续查看同栏目最近更新的文章。

更多
如何在PostgreSQL 16中创建带安全限定符的SQL视图详细教程
数据库 · 2026-06-27

如何在PostgreSQL 16中创建带安全限定符的SQL视图详细教程

先说几个核心判断:PostgreSQL 16 的安全视图,不是靠某个内置参数或语法开关就能一劳永逸解决的。它需要一套组合拳来保障——权限、schema 隔离、行级策略,少一个都不行。 PostgreSQL 16 安全视图的“三重卡死”机制 PostgreSQL 16 本身并不支持带参数的视图。

SQL视图定义中为何不建议使用SELECT * 而应明确列名
数据库 · 2026-06-27

SQL视图定义中为何不建议使用SELECT * 而应明确列名

从语法层面来看,在SQL视图定义中使用SELECT *本身并不构成语法错误。然而,从数据库设计与架构优化的角度审视,这种做法几乎等同于主动放弃了对于输出结果集的精确掌控——视图一旦创建,其列名、列顺序以及列数量理应是明确且固定的,而*通配符却让这一切变成了运行时才揭晓的未知数。视图列结构会因底层表变

SQL Server GROUP BY非聚合列报错解决方法
数据库 · 2026-06-27

SQL Server GROUP BY非聚合列报错解决方法

SQL Server 对查询的模糊性零容忍,态度极为明确。一旦 SELECT 列表中包含非聚合列且该列未被 GROUP BY 子句引用,SQL Server 便会立即抛出“列名无效”错误,绝不妥协、猜测或回退。这种严格虽然让新手感到棘手,但也迫使开发者正视查询语义的边界。 然而,许多开发者在遭遇此错

利用SQL嵌套查询检查日期区间重叠有效性
数据库 · 2026-06-27

利用SQL嵌套查询检查日期区间重叠有效性

好的,我将以一位资深数据库专家的视角,对原文进行人性化重写,保留所有核心信息、逻辑结构与图片,同时去除AI腔调,让语言更自然、有节奏,并谨慎控制第一人称的使用。 --- 日期区间重叠检查,这事儿的坑比想象的多。写 SQL 时,很多人总想着先写个函数或者建个临时表来比对,其实没必要——直接上自连接加个

Oracle 12c RAC环境下RMAN恢复共享数据文件
数据库 · 2026-06-27

Oracle 12c RAC环境下RMAN恢复共享数据文件

在RAC环境下使用RMAN恢复共享数据文件,很多DBA第一次遇到时都会感到棘手:备份文件明明完整,执行RESTORE DATABASE却报ORA-01102或ORA-01507。别紧张,这并非命令错误,而是RAC的共享存储与多实例并发机制与RMAN恢复流程存在根本性的不兼容。 RMAN在RAC下无法