MyBatis防止SQL注入原理:预编译机制与#{} vs ${}区别
MyBatis通过底层JDBC的PreparedStatement实现预编译功能来防御SQL注入攻击。核心在于区分安全的 #{} 占位符与不安全的 ${} 字符串拼接,了解其执行阶段差异对于编写安全代码至关重要。
SQL注入风险与MyBatis应对机制
SQL注入是一种针对数据驱动应用的攻击手段,恶意用户通过在表单或URL中注入如 ' or '1'='1' 这样的SQL片段,试图绕过参数校验从而篡改数据库内容。SQL注入攻击主要作用于SQL语句的编译过程。因此,只要能够隔离SQL逻辑与数据内容,即可有效阻断此类攻击。
第1步:理解预编译的工作原理
MyBatis作为一款半自动化的ORM框架,其SQL语句需手动编写。为防止注入,框架默认启用了预编译功能。以查询语句为例:
<select id="getBlogById" resultType="Blog" parameterType="int">
SELECT id, title, author, content
FROM blog
WHERE id = #{id}
</select>
当传入参数时,无论输入何种值,最终发送给数据库执行的SQL始终如下。其中 ? 是占位符:
SELECT id, title, author, content FROM blog WHERE id = ?
这一过程的底层依赖于 java.sql.PreparedStatement。MyBatis在SQL执行前会将带有占位符的SQL模板发送给数据库进行语法分析、生成执行计划并编译。由于语法结构在传入具体参数前已固定,后续传入的值仅被视为字符串字面量,而非可执行的SQL命令,从而从根本上避免了SQL注入。
#{ }与$ { }的本质区别
尽管MyBatis具备防注入能力,但并非所有参数格式都能触发此保护机制。开发者需严格区分两种参数绑定方式:
1. 安全的预编译:#{}
- 处理方式:相当于JDBC中的
PreparedStatement。 - 执行效果:将参数视为文本或数字,由数据库驱动自动添加引号并转义。
- 安全性:经过预编译,绝对安全。
2. 非安全的字符串替换:$ {}
当使用 ${} 时,MyBatis会直接将变量值拼接到SQL语句中,然后再整体发送给数据库进行编译。例如:
<select id="orderBlog" resultType="Blog" parameterType="map">
SELECT id, title, author, content
FROM blog
ORDER BY ${orderParam}
</select>
若 orderParam 的值为 id,最终执行的SQL为:
SELECT id, title, author, content FROM blog ORDER BY id
由于未启用预编译占位符机制,攻击者可通过传入 id DESC; DROP TABLE blog; 等恶意组合直接改变SQL语义,导致严重的注入风险。
特殊场景下的处理策略
虽然 ${} 存在安全风险,但在需要动态指定表名、列名或排序字段且无法使用占位符的场景下,仍必须使用 ${} 。此时,安全防线需从框架层转移至业务层:
- 白名单校验:确保输入的参数值完全匹配预期的枚举列表(如固定的列名集合)。
- 类型与长度检查:验证输入是否为合法的标识符字符,并限制长度。
- 避免直接拼接:严禁直接使用不可信的用户输入作为
${}的内容。
结论与建议
在编写MyBatis映射语句时,请遵循以下原则:
1. 优先且尽量使用 #{} 进行参数绑定,享受预编译带来的安全性与性能提升。
2. 仅在涉及动态表名、列名等强制需求时使用 ${} ,并务必实施严格的后端人工过滤或白名单机制。
以上就是MyBatis防止SQL注入原理的详细内容,更多关于MyBatis安全开发技巧的资料请关注本站其它相关文章!
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









