Bootstrap

从程序被SQL注入来MyBatis 再谈 #{} 与 ${} 的区别

目录

缘由

最近在的一个项目上面,发现有人在给我搞 SQL 注入,我真的想说我那么点资源测试用的阿里云服务器,个人估计哈,估计能抗住他的请求。狗头.png

系统上面的截图
系统上面的截图

数据库截图
数据库截图

说句实在的,看到这个之后我立马就是在想啊,现在我们都是用的成熟的ORM 框架,一般调用 ORM 框架的方法操作数据库是不会有问题的。

后面又让我想起来了,#{}${} 的区别,这里总结下,说一句通俗易懂的话:使用 #{} 可以防止SQL注入,使用 ${} 就会出现 SQL 注入。

代码理解

咱们都是程序员,都喜欢说,别给我说那么多废话,show me the code,那么我们直接看代码把,我们通过伪代码来理解下:

 public void test(String name, String id){
     String sql = "update orders set name = "+name+" where id = ?";
 }

这里我为了方便与说明,就是使用上面的一个SQL 来解析,从上面的 SQL 可以看到两点信息:

  • name: 是直接使用了拼接字符串的方式,这里就是类似于 ${} 做的事情
  • id:使用了 ? 做为占位符,做了一次预处理,先去编译SQL,后面再来做参数化操作,这个是 #{} 的具体原理

如果是 name 被恶意传入了 SQL 代码,比如:

") OR (SELECT*FROM(SELECT(SLEEP(3)))hnao) limit 1#
-- 或者
" union select 1-- 

那么解析出来的 SQL 就会变成:


update orders set name = ) OR (SELECT*FROM(SELECT(SLEEP(3)))hnao) limit 1

update orders set name = union select 1

虽然上面比较难看,因为我这里举例是 update,如果是查询呢:

select name from orders  union select 1-- 

那这样子是不是就出问题了?其实只要他成功一次,那么就可能窃取到我们数据库的信息了。

#{}${} 的区别

这里我也去百度了下,然后整理一下大致的区别,在 MyBatis 中,#{}${} 都是用于在 SQL 语句中插入参数的占位符,但它们之间有着显著的区别。

  1. 预编译与安全性

    • #{}:这是一个预编译(PreparedStatement)的占位符。当 MyBatis 在解析 XML 映射文件或注解中的 SQL 时,它会为 #{} 中的参数生成一个 PreparedStatement,并使用 ? 作为占位符。这意味着 SQL 语句会被预编译,并且在执行时会使用参数化查询,这样可以防止 SQL 注入攻击。
    • ${}:这是一个简单的字符串替换。MyBatis 会直接替换 ${} 中的内容为 SQL 语句中的相应部分。这意味着 SQL 语句不会被预编译,而是会动态地构建和执行。这可能会导致 SQL 注入攻击,因为它允许不受限制的字符串替换。
  2. 用法

    • #{}:通常用于插入参数值,例如列值、条件值等。
    • ${}:通常用于插入 SQL 片段,如表名、列名、动态 SQL 语句等。
  3. 动态 SQL

    • 在 MyBatis 中,${} 更多地用于构建动态 SQL,因为它允许直接替换 SQL 语句中的任何部分。然而,由于这种灵活性,它也增加了 SQL 注入的风险。
    • #{} 在动态 SQL 中通常用于插入参数值,以确保安全性。
  4. 类型处理器

    • 对于 #{} 插入的参数,MyBatis 会使用相应的类型处理器(Type Handler)来确保参数与数据库中的列类型匹配,并进行必要的类型转换。
    • 而对于 ${},由于它直接替换 SQL 语句中的部分,因此不会使用类型处理器。
  5. 注意事项

    • 尽可能使用 #{} 来插入参数值,以确保 SQL 语句的安全性和性能。
    • 如果确实需要使用 ${}(例如,插入表名或列名),请确保传入的字符串是安全的,并且不包含任何来自不受信任的来源的内容。
    • 在使用动态 SQL 时,要特别注意 SQL 注入的风险,并采取相应的预防措施。

总之,#{}${} 在 MyBatis 中各有其用途,但 #{} 通常更安全、更可靠,并应优先使用。

;