游乐游手机版
首页/编程语言/文章详情

MyBatis 安全指南:#{} 与 ${} 的本质区别与正确用法

时间:2026-10-10 13:51
在 MyBatis 开发中, {} 与 ${} 的选择直接决定了应用是否面临 SQL 注入风险。本文从底层参数绑定机制出发,解析两者在预编译与字符串拼接上的本质差异,明确 ${} 导致注入的根源,并给出在动态 SQL、排序等场景下的安全选型策略。通过日志验证与白名单校验,帮助开发者在保持灵活性的同时

底层机制:预编译与字符串拼接的安全边界

MyBatis 中 #{} 与 ${} 的核心差异,源于它们处理 SQL 参数的方式截然不同。#{} 采用预编译参数绑定(PreparedStatement),MyBatis 在解析 SQL 时会将占位符替换为数据库驱动的问号“?”,随后由 JDBC 驱动在数据库层进行安全的类型转换与转义。这意味着无论用户输入包含何种特殊字符,数据库都将其视为纯文本值,而非可执行的 SQL 指令。相反,${} 执行的是纯粹的字符串拼接,MyBatis 会在 SQL 编译前直接将变量值替换到 XML 或注解的文本中。例如,当传入参数为 admin' OR '1'='1 时,#{} 生成的 SQL 仍为安全的 WHERE name = ?,而 ${} 则会直接拼接到语句中,破坏原有 SQL 结构。这种机制差异直接决定了两者在面对不可信用户输入时的安全边界:#{} 天然具备防注入能力,而 ${} 则将拼接风险完全暴露给开发者。

需要一张真实的MyBatis开发或SQL调试界面配图,能够直观看到参数绑定与SQL文本拼接的差异。
MyBatis 日志中展示了 Preparing 占位符与 Parameters 参数绑定,并可还原为实际 SQL。

风险剖析:为什么 ${} 是 SQL 注入的高发区

${} 之所以成为 SQL 注入的高发区,根本原因在于它绕过了数据库的预编译保护,直接将外部输入嵌入到 SQL 语句的语法结构中。当开发者在动态查询中误用 ${} 处理用户可控参数时,恶意输入即可轻易闭合原有引号并追加非法指令。典型危险场景包括:将前端传入的排序字段直接用于 ORDER BY ${column},或将搜索关键词拼接到 LIKE '%${keyword}%' 中。若攻击者传入 id; DROP TABLE users; --,数据库将直接执行破坏性操作。需要明确的是,#{} 并非万能替代品。由于预编译机制的限制,#{} 只能用于绑定 SQL 语句中的“值”(Value),无法用于替换表名、列名、排序关键字(ASC/DESC)或动态拼接的 SQL 片段。在这些必须动态生成 SQL 结构的场景中,盲目替换为 #{} 会导致语法错误,因此开发者必须正视 ${} 的不可替代性,并在必须使用时建立严格的输入过滤机制。

需要一张真实的数据库SQL注入测试或开发调试场景配图,突出恶意输入进入动态SQL的风险。
Burp Suite Intruder 展示 SQL 注入 payload 批量测试及不同响应结果。

实战选型:值用 #{},结构用 ${} 且严控

在实际开发中,参数选型应遵循“值用 #{},结构用 ${} 且严控”的核心原则。对于所有 WHERE 条件过滤、INSERT/UPDATE 的字段赋值以及 LIKE 模糊查询,必须无条件使用 #{},以利用预编译机制彻底阻断注入路径。当业务需求涉及动态表名、动态列名、排序方向(ASC/DESC)或分页偏移量时,由于数据库预编译协议不支持对 SQL 标识符进行参数化,只能使用 ${}。此时必须引入白名单校验机制:例如在排序场景中,预先定义允许排序的字段集合 Set validColumns = Set.of("create_time", "status", "id"),在拼接前校验用户输入是否严格匹配白名单,若不匹配则抛出异常或降级为默认排序。对于复杂的动态 SQL,应优先使用 MyBatis 提供的 、、 等标签进行逻辑控制,而非依赖字符串拼接。通过“白名单拦截+动态标签替代”的组合策略,可在满足灵活性的同时守住安全底线。

需要一张真实的代码编辑器中MyBatis Mapper XML或注解SQL示例配图,体现参数查询与动态排序等场景。
IDE 中的 MyBatis Mapper XML 展示 where、choose、when 与 #{} 参数条件查询。

验证与避坑:从 SQL 日志到安全测试

验证 SQL 安全性不能仅依赖代码审查,必须结合运行期日志与主动测试。首先,在开发环境开启 MyBatis 的 SQL 日志打印(如配置 logging.level.org.mybatis=DEBUG),观察实际发送给数据库的完整 SQL 语句。若日志中显示 #{} 被替换为带引号的字符串且无语法异常,说明绑定正常;若 ${} 拼接后出现未闭合引号或多余关键字,则存在注入隐患。其次,应使用边界值与标准注入 Payload(如 ' OR '1'='1、admin'; DROP TABLE users;--)进行自动化单元测试,验证恶意输入是否被拦截或转义。在避坑实践中,常见误用包括在 WHERE 子句中滥用 ${}、未对动态列名做枚举校验、以及将 ${} 与 混用导致空值拼接报错。代码审查时应建立检查清单:全局搜索 ${ 关键字,确认其仅用于标识符替换;强制要求所有 ${} 变量必须经过白名单过滤或枚举限制;对涉及多租户或敏感数据的查询,增加 SQL 审计拦截器。通过日志追踪、自动化测试与规范化审查,可系统性消除动态 SQL 的安全盲区。

需要一张真实的开发测试环境配图,包含SQL日志、单元测试或安全测试结果,用于表现验证与排查过程。
JUnit 测试代码使用恶意输入验证 SQL 注入是否被安全地当作普通参数处理。
来源:workshop:c7d4b894042a492595f0ff1f61891dd6:site:2
上一篇Linux 日志轮转实战:logrotate 配置与压缩指南 下一篇网络安全:GraphQL注入与深度查询攻击
本站内容用于信息整理与展示,如有侵权或内容问题请及时联系处理。

相关推荐

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

同类最新

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

更多
35岁转行网络安全:从经验复用到实战落地的可行性评估
编程语言 · 2026-10-10

35岁转行网络安全:从经验复用到实战落地的可行性评估

35岁转行网络安全并非不可行,但核心在于将过往经验转化为安全领域的差异化优势。本文从岗位匹配度、技能学习顺序、实战验证闭环、求职策略及常见误区五个维度,提供一套可执行的转行评估框架与行动指南,帮助读者理性判断投入产出比,避开无效学习陷阱。

网络安全行业前景分析:技术演进与市场机遇
编程语言 · 2026-10-10

网络安全行业前景分析:技术演进与市场机遇

围绕2026年网络安全行业的发展变化,从市场需求、技术演进、细分赛道和企业落地四个层面展开,帮助读者理解行业增长逻辑、识别重点技术方向,并建立评估市场机遇与风险的基本框架。 OWASP China +2 IDC +2

2026网络安全求职全景:从岗位拆解到实战作品集构建
编程语言 · 2026-10-10

2026网络安全求职全景:从岗位拆解到实战作品集构建

本文基于2026年网络安全行业招聘趋势,深入剖析安全运维、攻防渗透、云安全等核心岗位的技术栈差异与能力侧重。文章不仅梳理了从基础网络知识到高级攻防演练的学习路径,更提供了“以终为始”的求职策略:通过拆解JD反向验证技能缺口,并指导如何将CTF经历、HomeLab实验转化为具有说服力的项目作品集,帮助

2024安全攻防实战:从勒索软件到AI治理的破局与重构
编程语言 · 2026-10-10

2024安全攻防实战:从勒索软件到AI治理的破局与重构

2024年的网络安全已从单纯的技术对抗演变为业务连续性的生死博弈。本文基于ENISA、微软及世界经济论坛的最新报告,深入剖析勒索软件的“双重勒索”演变、身份凭证成为首要攻击面的现状,以及生成式AI带来的攻防不对称性。文章进一步拆解企业如何从被动防御转向“发现-保护-检测-响应-恢复”的闭环体系,重点

网站编程AI工具测评:提升开发效率的辅助软件推荐
编程语言 · 2026-10-10

网站编程AI工具测评:提升开发效率的辅助软件推荐

围绕网站开发中的实际需求,对AI编程辅助工具进行分类、操作体验与效果验证,帮助读者快速判断哪些工具真正能提升开发效率,并避开代码质量、隐私、安全与过度依赖等常见问题。