题目
MyBatis动态SQL中如何防止SQL注入?请结合#{}和${}的区别说明
信息
- 类型:问答
- 难度:⭐⭐
考点
动态SQL编写,SQL注入防范,MyBatis参数处理原理
快速回答
在MyBatis中防止SQL注入的核心是正确使用参数占位符:
- 优先使用#{}: 采用预编译机制,将参数值安全地绑定到SQL中
- 避免使用${}: 直接拼接SQL片段,存在注入风险
- 特殊场景处理: 动态表名/列名等必须使用${}时,需严格过滤参数
1. 核心原理说明
#{} 工作原理:
- MyBatis会创建
PreparedStatement,使用?占位符 - 参数值在SQL执行前被安全绑定,特殊字符会被转义
- 示例最终SQL:
SELECT * FROM users WHERE name = ?
${} 工作原理:
- 直接替换为字符串值,生成原生SQL
- 示例:
ORDER BY ${columnName}可能生成ORDER BY name - 若参数值为:
name; DROP TABLE users--将导致灾难性后果
2. 代码示例对比
<!-- 安全示例 -->
<select id="findUser" resultType="User">
SELECT * FROM users
WHERE name = #{name} <!-- 预编译安全 -->
ORDER BY ${sortColumn} <!-- 需手动校验sortColumn值 -->
</select>
<!-- 危险示例 -->
<select id="unsafeQuery">
SELECT * FROM ${tableName} WHERE id = ${id} <!-- 双重注入风险 -->
</select>3. 最佳实践
- 默认规则: 所有用户输入必须使用
#{} - ${} 使用场景:
- 动态表名/列名:
SELECT * FROM ${tableName} - SQL关键字:
ORDER BY ${column} ${direction}
- 动态表名/列名:
- 安全措施:
- 白名单校验:对${}参数值进行枚举匹配
- 示例:
if(!Arrays.asList("name","age").contains(column)) throw new Exception("Invalid column") - 避免拼接用户输入:绝不将用户输入直接用于表名/列名
4. 常见错误
- 混淆使用场景:在WHERE条件中使用
${value} - 错误示例:
WHERE username = '${username}'(可注入) - 未校验的排序参数:
ORDER BY ${sort}允许注入1; DELETE FROM users - 批量操作时误用:
DELETE FROM table WHERE id IN (${ids})应改用<foreach>动态SQL
5. 扩展知识
- 动态SQL辅助: 使用
<where>,<if>等标签避免手动拼接<select id="search"> SELECT * FROM users <where> <if test="name != null">AND name = #{name}</if> <if test="age != null">AND age = #{age}</if> </where> </select> - 底层防护: 结合数据库的预编译机制(如MySQL的
PREPARE/EXECUTE) - 框架增强: 整合MyBatis-Plus的
SqlInjector进行自动化过滤