FlawOpen

flawopen.com/Java 中的 SQL Injection/MyBatis

MyBatis ${} 与 #{}:防止 SQL Injection

参考页面
简答

把 #{value} 想成预处理语句中的安全值槽:数据库把它当作数据。${value} 会在预处理前把文本复制进 SQL。

绑定值与原始 SQL 文本

不安全 — ${} 原始文本
<select id="getUser">
  SELECT * FROM users
  WHERE id = ${id}
</select>
// Raw substitution: the SQL text changes
绑定值 — #{}
<select id="getUser">
  SELECT * FROM users
  WHERE id = #{id}
</select>
// MyBatis creates a PreparedStatement parameter

MyBatis 为什么提供两种语法

#{} 使用 JDBC PreparedStatement 参数绑定值。参数不能替代表名、列名或排序方向等 SQL 语法。${} 会直接替换文本且不转义。优先使用静态语句或 <choose> 分支;否则从服务端白名单选择完整标识符,绝不插入任意请求文本。

必须使用动态标识符时

对白名单中的完整 SQL 标识符或片段进行选择

绑定参数不能替代标识符。排序时将用户选择映射为服务端固定 SQL 片段(或使用 <choose>)。不要把客户端列名、表名或 ASC/DESC 直接传给 ${}。转义不能使任意 SQL 片段安全。

如何审查 MyBatis mapper

rg -n -F '${' --glob '*.xml' --glob '*.java' .
检查 XML 和注解中的匹配项并追踪值的来源。值用 #{};${} 必须来自服务端有限白名单。搜索只能找到语法,无法发现所有间接 SQL 构造。

常见问题

注解式 mapper 也适用吗?

适用。注解使用相同规则。@Select 中的 ${column} 插入原始文本;值用 #{value} 绑定,标识符须白名单。

搜索 ${} 就能证明代码安全吗?

不能。检查搜索结果;SQL 可能在别处拼接。为 XML 和注解增加检查,并测试排序选项只映射到预期固定片段。

参考资料

另请参阅 SQL 注入(CWE-89):原理与各编程语言的防护方法 — flawopen.com MySQLi 与 PDO:安全使用预处理语句 NHibernate 能防止 SQL 注入吗? MongoDB NoSQL 注入:安全查询过滤器 数字 ID 使用字符串拼接也会导致 SQL 注入吗? 什么是参数化查询? Prisma $queryRaw 能防止 SQL 注入吗?