缘由
最近在的一个项目上面,发现有人在给我搞 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 语句中插入参数的占位符,但它们之间有着显著的区别。
-
预编译与安全性:
#{}
:这是一个预编译(PreparedStatement
)的占位符。当 MyBatis 在解析 XML 映射文件或注解中的 SQL 时,它会为#{}
中的参数生成一个 PreparedStatement,并使用?
作为占位符。这意味着 SQL 语句会被预编译,并且在执行时会使用参数化查询,这样可以防止 SQL 注入攻击。${}
:这是一个简单的字符串替换。MyBatis 会直接替换${}
中的内容为 SQL 语句中的相应部分。这意味着 SQL 语句不会被预编译,而是会动态地构建和执行。这可能会导致 SQL 注入攻击,因为它允许不受限制的字符串替换。
-
用法:
#{}
:通常用于插入参数值,例如列值、条件值等。${}
:通常用于插入 SQL 片段,如表名、列名、动态 SQL 语句等。
-
动态 SQL:
- 在 MyBatis 中,
${}
更多地用于构建动态 SQL,因为它允许直接替换 SQL 语句中的任何部分。然而,由于这种灵活性,它也增加了 SQL 注入的风险。 #{}
在动态 SQL 中通常用于插入参数值,以确保安全性。
- 在 MyBatis 中,
-
类型处理器:
- 对于
#{}
插入的参数,MyBatis 会使用相应的类型处理器(Type Handler)来确保参数与数据库中的列类型匹配,并进行必要的类型转换。 - 而对于
${}
,由于它直接替换 SQL 语句中的部分,因此不会使用类型处理器。
- 对于
-
注意事项:
- 尽可能使用
#{}
来插入参数值,以确保 SQL 语句的安全性和性能。 - 如果确实需要使用
${}
(例如,插入表名或列名),请确保传入的字符串是安全的,并且不包含任何来自不受信任的来源的内容。 - 在使用动态 SQL 时,要特别注意 SQL 注入的风险,并采取相应的预防措施。
- 尽可能使用
总之,#{}
和 ${}
在 MyBatis 中各有其用途,但 #{}
通常更安全、更可靠,并应优先使用。