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

相关推荐
碎像22 分钟前
uni-app实战教程 从0到1开发 画图软件 (学会画图)
前端·javascript·css·程序人生·uni-app
Hilaku39 分钟前
从“高级”到“资深”,我卡了两年和我的思考
前端·javascript·面试
WebInfra1 小时前
Rsdoctor 1.2 发布:打包产物体积一目了然
前端·javascript·github
用户52709648744901 小时前
SCSS模块系统详解:@import、@use、@forward 深度解析
前端
兮漫天1 小时前
bun + vite7 的结合,孕育的 Robot Admin 【靓仔出道】(十一)
前端·vue.js
xianxin_1 小时前
CSS Text(文本)
前端
秋天的一阵风1 小时前
😈 藏在对象里的 “无限套娃”?教你一眼识破循环引用诡计!
前端·javascript·面试
电商API大数据接口开发Cris1 小时前
API 接口接入与开发演示:教你搭建淘宝商品实时数据监控
前端·数据挖掘·api
用户1409508112802 小时前
原型链、闭包、事件循环等概念,通过手写代码题验证理解深度
前端·javascript
汪子熙2 小时前
错误消息 Could not find Nx modules in this workspace 的解决办法
前端·javascript