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 的参数机制,是构建安全系统的基础。永远不要相信任何用户输入! 🧱

相关推荐
随风一样自由14 分钟前
【前端工程化】前端工程化解耦实践:从问题分析到架构重构
前端·重构·架构
vortex516 分钟前
Web 渗透测试:未授权与越权漏洞全流程挖掘思路
前端
我有满天星辰32 分钟前
【前端模块化】前端模块化演进之路:从脚本堆砌到标准统一
前端
我有满天星辰38 分钟前
Vue 3 响应式双雄:ref 与 reactive 完全指南
前端·javascript·vue.js
随风一样自由9 小时前
【前端独角兽】刚进入前端领域的前端开发用jQuery还是Vue3?
前端·javascript·vue3·jquery
IMPYLH10 小时前
HTML 的 <data> 元素
前端·html
YHHLAI10 小时前
[特殊字符] 面试中的 Promise —— 从入门到精通
前端·javascript·面试
观远数据11 小时前
ChatBI选型对比:从意图识别到SQL修复,六个维度打分决定是否值得投产
数据库·人工智能·sql
嘉&年华11 小时前
【第008篇】通过dexp和dimp命令导出和导入dmp文件(适用于达梦数据库)
数据库·sql
kyriewen12 小时前
我扒了 GPT-5.6 的全部编程跑分——有一项 OpenAI 没敢放出来
前端·gpt·ai编程