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

相关推荐
We་ct2 分钟前
LeetCode 162. 寻找峰值:二分高效求解
前端·算法·leetcode·typescript·二分·暴力
HWL56795 分钟前
uni-app的生命周期
前端·vue.js·uni-app
softbangong6 分钟前
829-批量提取各子文件夹下文件到一级目录
java·服务器·前端·自动化工具·批量文件处理·文件提取工具·文件夹整理
李剑一7 分钟前
别再瞎写 Cesium 可视化!热力图 + 四色图源码全公开,项目直接复用!
前端·vue.js·cesium
SuperEugene8 分钟前
Vue3 + Vue Router + Pinia 路由守卫规范:beforeEach 应做 / 不应做,避死循环、防重复请求|状态管理与路由规范篇
开发语言·前端·javascript·vue.js·前端框架
Greg_Zhong10 分钟前
Css知识之伪类和伪元素
前端·css
Mintopia11 分钟前
GPT-5.3-Codex 底层逻辑是什么,为什么编码强?
前端·人工智能·ai编程
Mintopia12 分钟前
Opus 模型凭什么收费贵,与其他模型对比理由是什么?
前端·人工智能
东东__net14 分钟前
js逆向与谷歌加密库
开发语言·前端·javascript
上海云盾-小余15 分钟前
应用层漏洞实战防护:SQL 注入、XSS、文件上传漏洞一站式加固方案
数据库·sql·xss