转载来源:mybatis是如何防止SQL注入的 - 王的微笑 - 博客园 (cnblogs.com) https://www.cnblogs.com/jokmangood/p/11705850.html
1.什么是SQL注入?
SQL注入是一种代码注入技术,用于攻击数据驱动的应用,恶意的SQL语句被插入到执行的实体字段中。
常见方式:攻击者在界面的表单信息或者URL上输入一些奇怪的SQL片段(例如 "or '1' ='1' " 这样的语句),有可能入侵参数校验不足的程序。
在一些安全性要求很高的应用中(如银行软件),经常使用将SQL语句全部替换成存储过程这样的方式,来防止SQL注入,这当然是一种很安全的方式,但在我们日常开发中可能不需要这种死板的方式。
2.mybatis是如何做到防止SQL注入的?
mybatis框架虽然是一款半自动化的持久层框架,但是它启用了预编译的功能,在SQL执行前,会将SQL发送给数据库进行编译;执行时,直接使用编译好的SQL,替换占位符 "?" 就可以了。因为SQL注入只能对编译过程起作用,所以mybatis能能好的避免了SQL注入的问题。
3.底层实现原理?
主要是JDBC中的PreparedStatement类在起作用,PreparedStatement是Statement的子类,它的对象包含了编译好的SQL。这种 “准备好” 的方式不仅能提高安全性,而且在多次执行同一个SQL时,因为SQL已经编译好,会大大提升效率。
简单来说,#{}是经过预编译的,是安全的;${}是未经过预编译的,仅仅是取变量的值,是非安全的,存在SQL注入。
如果order by 语句后用了${},不做任何处理的时候是存在SQL注入危险的。需要手动过滤下输入的内容,比如判断下输入的参数长度是否正常(注入语句一般很长),更精确的过滤则可以查询一下输入的参数是否存在预期的参数集合中。
4.#{} 和 ${} 的区别?
两个SQL:
<select id="selectByNameAndPassword" parameterType="java.util.Map" resultMap="BaseResultMap">
select id, username, password, role
from user
where username = #{username,jdbcType=VARCHAR}
and password = #{password,jdbcType=VARCHAR}
</select>
<select id="selectByNameAndPassword" parameterType="java.util.Map" resultMap="BaseResultMap">
select id, username, password, role
from user
where username = ${username,jdbcType=VARCHAR}
and password = ${password,jdbcType=VARCHAR}
</select>
1)#将传入的数据都当成一个字符串,会对自动传入的数据加一个双引号;
如:where username = #{username},如果传入的值是111,那么解析成sql的时候为 where username = "111",如果传入的是id,则解析成sql为 where username = "id"。
2)$将传入的数据直接显示生成在sql中;
如:where username = #{username},如果传入的值是111,那么解析成sql的时候为 where username = 111;
如果传入的值为;drop table user,则解析成sql的时候为 select id, username, password, role from user where username=;drop table user;
3)#{}能很大程度上防止sql注入,${}无法防止sql注入;
4)${}方式一般用于传入数据库对象,例如传入表名;
5)一般能用#{}就别用${},若不得不使用 ${xxx} 这样的参数,要手工做好过滤工作,防止SQL注入;
6)在mybatis中,"${xxx}"格式的参数会直接参与SQL编译,不能避免注入攻击。但是涉及到动态表名和列名的时候,只能使用 "${xxx}" 这样的参数。所以,这样的参数需要我们在代码中手工进行处理来防止注入。