Mybatis 的 Mapper.xml 语句中 parameterType 向SQL语句传参有两种方式:#{ } 和 ${ } ;
简单来说:
-
#{ }:是预编译处理,能有效防止 SQL 注入,适用于绝大多数场景。
-
${ }:是字符串拼接,存在 SQL 注入风险,仅在特殊场景下谨慎使(如order by)。
如何排查
-
检查是否有 ${ 号;
如果使用的是ide代码编辑器,那么可以通过全局搜索 ${ , 快速定位到使用 ${ } 拼接SQL的语句,在去找到外部传入参数的入口,闭合sql证明即可。 -
检查是否有
order by语句;
同样的在搜索是否使用 order by 排序语句,且是否有外部参数,未过滤就直接传递到order by语句里面来。
为什么#{ }安全?
#{ } 就类似JDBC的预编译,所以安全。类似如下SQL语句:
- 使用 ${ }效果是:
select * from testtable where id="1" or true or id # 1后面就是被攻击者恶意构造的字符

- 而使用#{ } 的效果是:
select * from testtable where id="1\" or true or id\"" # 1后面就是被攻击者恶意构造的字符

什么情况下用不了#{ }?
在order by 排序语句,且排序的参数是外部输入,这种情况不行,为什么?
因为SQL会报错。
先复习一下order by的用法:
order by就是一个排序的工具。
# 这个1就是指第一个列索引,也可以写id (列索引)
select * from testtable ORDER BY 1 ASC #ASC表示按升序排序,DESC表示按降序排序
# 两个代码是一样的
select * from testtable ORDER BY id ASC #ASC表示按升序排序,DESC表示按降序排序

对于order by 我们是用不了#{ }的,因为用了这个就会被自动转换成字符串,自动加引号,导致语句不生效。
<select id="selectStudentsByName" resultType="Student">
select id,name,age,score from student order by #{column}
</select>
<!--编译出来的结果如下:-->
select * from table order by 'column'

会发现加上“ ” 双引号符号后,就没法正常排序了。
语法错误: ORDER BY 子句后面应该跟的是列名或表达式,而不是一个字符串值。数据库会将 'age' 视为一个常量字符串,而不是一个列标识符。
执行结果错误: 即使某些数据库(如 MySQL)没有抛出语法错误,这种语句也会正常执行,但它的含义完全变了。它不再是“按 id 列降序排列”,而是“按一个固定的常量值排序”。这会导致所有行的排序依据都相同,返回的结果集看起来像是随机的或完全无序的,失去了排序的意义。
如何解决?
现在,我们改用 ${}:
使用${}后,MyBatis就不会修改或转义改字符串。
<!-- 正确写法 (但需注意安全!) -->
<select id="selectUsersOrderedByColumn" resultType="User">
SELECT * FROM user ORDER BY ${sortColumn} DESC
</select>
SELECT * FROM user ORDER BY age DESC;
这条语句完全符合 SQL 语法规范,能够正确地对 age 列进行排序。
虽然 ${} 解决了 ORDER BY 的动态问题,但它也带来了巨大的 SQL 注入攻击风险。会导致潜在的SQL注入攻击。所以一般需要严格的校验输入,采用白名单机制。
浙公网安备 33010602011771号