MyBatis #{} 与 ${} 有什么区别?为什么预编译能防止SQL注入?

🔐 前言

想象一下:你开了一家银行,客户可以凭姓名和密码查询余额。有一天,黑客在姓名框输入 ' OR '1'='1' --,密码随意填。若系统将输入直接拼接到 SQL 中,查询将变成:

sql 复制代码
SELECT balance FROM accounts 
WHERE username = '' OR '1'='1' -- ' AND password = '随意密码'

由于 OR '1'='1' 恒为真,且 -- 注释了后续条件,攻击者就可绕过验证,获取所有账户信息!

这就是常见的严重漏洞 ------ SQL 注入攻击


❗ 为什么传统 SQL 拼接如此危险?

以 MyBatis 为例,以下是一个错误示范:

xml 复制代码
<!-- 直接拼接用户输入,存在高风险 -->
<select id="findUser" resultType="User">
  SELECT * FROM users 
  WHERE username = '${username}' AND password = '${password}'
</select>

如果用户输入:

text 复制代码
username = ' OR 1=1 --

执行的 SQL 实际变为:

sql 复制代码
SELECT * FROM users 
WHERE username = '' OR 1=1 -- ' AND password = 'anything'

攻击结果:绕过密码验证,暴露所有用户数据!


🛡️ MyBatis 的救星:预编译 SQL(#{}

✅ 正确写法

xml 复制代码
<!-- 使用 #{},预编译避免注入 -->
<select id="findUser" resultType="User">
  SELECT * FROM users 
  WHERE username = #{username} AND password = #{password}
</select>

⚙️ 预编译工作原理详解

  1. 发送模板:

    sql 复制代码
    SELECT * FROM users WHERE username = ? AND password = ?
  2. 数据库编译模板: 解析并生成执行计划(SQL 结构已固定)

  3. 绑定参数: 用户输入的 usernamepassword 作为数据绑定,不参与 SQL 语法结构

  4. 执行查询: 使用编译好的计划,安全执行

🔍 数据与指令的隔离示例

攻击者输入:' OR 1=1 --,执行效果如下:

sql 复制代码
SELECT * FROM users WHERE username = '\' OR 1=1 -- '

攻击失效! 数据库将输入视为普通字符串,而不是 SQL 指令。


🧾 ${}#{} 的终极对比手册

特性 ${}(文本替换) #{}(预编译参数)
工作原理 拼接字符串进入 SQL 占位符 ?,参数单独传入
安全性 ❌ 极易被注入攻击 ✅ 安全,防注入
特殊字符处理 不转义,容易破坏语法结构 自动转义,防止构造注入
数据类型处理 默认视为字符串 自动识别类型(数字、日期等)
执行计划复用 ❌ 每次拼接结果不同,无法复用 ✅ 可复用执行计划,性能更优
调试日志可读性 ✅ SQL 显示完整字符串 ⚠️ 仅显示 ? 占位符

🧰 实战建议:安全与灵活的平衡

✅ 绝对安全区

  • 所有 用户输入 (如查询条件、登录信息)必须使用 #{},禁止拼接

⚠️ 风险可控区

当使用 ${} 拼接动态字段(如表名、列名)时,需确保:

  • 参数来源非用户直接输入
  • 严格的白名单校验
  • 禁止包含 ;--\ 等危险字符

🚀 性能优化场景

分页、批量查询(如 IN 查询)可结合 <foreach> 标签实现,确保使用 #{} 包裹每个参数

xml 复制代码
<!-- IN 查询示例 -->
<select id="findByIds" resultType="User">
  SELECT * FROM users WHERE id IN
  <foreach collection="idList" item="id" open="(" separator="," close=")">
    #{id}
  </foreach>
</select>

✅ 总结:牢记 SQL 安全三原则

🚨 务必牢记:涉及用户输入的 SQL 查询,永远不要使用 ${} 拼接语句!

  1. 使用 #{} 保证数据与指令分离,防止 SQL 注入
  2. 使用 ${} 时必须建立在白名单参数可信的基础上
  3. 对日志、调试、性能有要求时,合理配合日志插件、安全策略使用

数据库安全无小事,一次 SQL 注入可能导致严重数据泄露。理解 MyBatis 的参数机制,是构建安全系统的基础。永远不要相信任何用户输入! 🧱

相关推荐
Lris-KK27 分钟前
【Leetcode】高频SQL基础题--180.连续出现的数字
sql·leetcode
二川bro39 分钟前
第25节:VR基础与WebXR API入门
前端·3d·vr·threejs
上单带刀不带妹1 小时前
Node.js 的模块化规范是什么?CommonJS 和 ES6 模块有什么区别?
前端·node.js·es6·模块化
缘如风1 小时前
easyui 获取自定义的属性
前端·javascript·easyui
诗书画唱1 小时前
【前端教程】JavaScript 实现图片鼠标悬停切换效果与==和=的区别
开发语言·前端·javascript
光影少年1 小时前
前端上传切片优化以及实现
前端·javascript·掘金·金石计划
喜葵1 小时前
前端安全防护深度实践:从XSS到供应链攻击的全面防御
前端·安全·xss
_r0bin_1 小时前
分片上传-
前端·javascript·状态模式
东北南西1 小时前
手写React状态hook
前端·javascript·react.js
诗书画唱1 小时前
【前端教程】JavaScript DOM 操作实战案例详解
开发语言·前端·javascript