侧边栏壁纸
博主头像
colo

欲买桂花同载酒

  • 累计撰写 1823 篇文章
  • 累计收到 0 条评论

MyBatis动态SQL中如何防止SQL注入?请结合#{}和${}的区别说明

2025-12-13 / 0 评论 / 4 阅读

题目

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 进行自动化过滤