SQL注入:MyBatis中#{}与${}安全差异与选型

Ruby
Ruby
发布时间:2026-09-10| 1人浏览过
原创

理解#{}与${}:参数绑定机制决定安全边界

在MyBatis框架中,#{}与${}的核心差异源于底层参数绑定机制的不同。#{}采用预编译参数绑定(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 批量测试及不同响应结果。

实际选型:查询参数、排序与动态SQL怎么用

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

需要一张真实的代码编辑器中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子句中滥用${}、未对动态列名做枚举校验、以及将${}与<if>混用导致空值拼接报错。代码审查时应建立检查清单:全局搜索${关键字,确认其仅用于标识符替换;强制要求所有${}变量必须经过白名单过滤或枚举限制;对涉及多租户或敏感数据的查询,增加SQL审计拦截器。通过日志追踪、自动化测试与规范化审查,可系统性消除动态SQL的安全盲区。

需要一张真实的开发测试环境配图,包含SQL日志、单元测试或安全测试结果,用于表现验证与排查过程。
JUnit 测试代码使用恶意输入验证 SQL 注入是否被安全地当作普通参数处理。
相关标签:

免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。

Python 相关产品和应用

全部下载
Selenium

Selenium

4.51.0 · Windows

用于浏览器自动化和 Web 测试的 Python 工具。

scikit-learn

scikit-learn

1.9.1 · Windows

常用 Python 机器学习算法与模型评估工具库。

Black

Black

26.10.1 · Windows

强调一致性的 Python 自动代码格式化工具。

Ruff

Ruff

0.17.1 · macOS

由 Rust 编写的高速 Python 代码检查和格式化工具。

pytest

pytest

9.1.1 · Windows

简洁而强大的 Python 自动化测试框架。

FastAPI

FastAPI

0.146.0 · Windows

高性能 Python API 框架,支持类型提示和自动接口文档。

Flask

Flask

3.1.3 · Windows

轻量灵活的 Python Web 应用框架。

Django

Django

6.1.2 · Windows

成熟的 Python Web 框架,适合构建内容站点和业务系统。

Spyder

Spyder

6.1.7 · Windows

面向科学计算与数据分析的 Python 集成开发环境。

SciPy

SciPy

1.18.1 · Windows

面向科学、工程和技术计算的 Python 算法库。