今天在优化一个查询的时候,关于一个SQL语句的问题,发现参数传进去是空的,导致条件查询失败了,查出来的是所有的数据。
刚开始是这样写的
<select id="selectPictureList" parameterType="map" resultMap="BaseResultMap">
select * from picture
<where>
<if test="pictureName != null and pictureName != ''">
picture_name like '%${pictureName,jdbcType=VARCHAR}%'
</if>
<if test="pictureOwner != null and pictureOwner != ''">
and picture_owner = #{pictureOwner,jdbcType=VARCHAR}
</if>
</where>
</select>
控制台打印的信息是:
Preparing: select * from picture WHERE picture_name like '%%' LIMIT ?
DEBUG [http-apr-8080-exec-5] - ==> Parameters: 10(Integer)
DEBUG [http-apr-8080-exec-5] - <== Total: 4
这里也确定参数是传到controller方法里面的,后来仔细检查之后,仍然没有看出来哪里有问题,于是开始百度,对比之后发现sql拼接的时候,在大括号里多了一个参数的类型,jdbcType=VARCHAR
,将这个删掉之后,条件查询就生效了。事情到这里并没有结束,我看到有人提出这样写不能防止sql注入的问题,看到这里有点蒙,之前确实听说过在SQL拼接时,要注意防止SQL注入的问题,但是什么是SQL注入,我现在真的忘了。。。。。。
于是开始查SQL注入的问题:
下面内容为参考:https://www.cnblogs.com/sdya/p/4568548.html 大神的博客整理,
这篇博客,介绍的也很详细:https://blog.csdn.net/lxzpp/article/details/80521332
所谓SQL注入,就是通过把SQL命令插入到Web表单递交或输入域名或页面请求的查询字符串,最终达到欺骗服务器执行恶意的SQL命令,比如先前的很多影视网站泄露VIP会员密码大多就是通过WEB表单递交查询字符暴出的,这类表单特别容易受到SQL注入式攻击.
用我自己的理解就是:
比如从登录模块来看,我们在将用户输入的条件在后台拼接成动态SQL查询时,如果用户按照我们设想的那样,用户名和密码都是正常的字符串,那么我们后台写的查询用户的语句:select * from user where user_name = '用户名' and password = '密码';
便可以正常确认是否存在此用户的信息。
但是这里怎么会出现SQL注入问题呢?
如果我们在用户名的输入框里输入的是:user ' or 1 = 1#
的情况下,后台在执行这句SQL的时候,将将自动拼接成了select * from user where user_name = ‘user ' or 1 = 1#' and password = '密码'
’`
而在mysql中,SQL语句中#后面的被当成注释了(在Oracle中--
后面是注释),而前面的查询中有一个恒成立的条件1=1
这就导致这条SQL语句查询的用户不是为空的,是存在的。此时便可以骗过服务器,模拟登陆成功的场景。
还有一种情况是在删除操作的时候,一般我们进行删除数据的时候,都是在url里拼接出要删除的数据的id什么的,delUserById.do?userId=1
这时,在不怀好意的人获取到请求连接之后,在加上一个小小的or,比如这样:userId=2 or 1
这样就有可能删除全部数据------sql注入就是通过类似的手段来破坏数据
看到这里我是忽然感觉后背一凉,原来我天天自认为自己操作简单的增删改查已经很熟练了,但是这些漏洞却从来没有注意过,如果这种灾难要是出现在项目中,可能就要卷铺盖走人了吧。。。。。
由此我们在设计SQL语句的时候就应该试着去避免这些坑。
对于上面的例子中,我们改如何改进呢?
<where>
<if test="pictureName != null and pictureName != ''">
picture_name like "%"#{pictureName}"%"
</if>
</where>
此时控制台打印的SQL语句为:
DEBUG [http-apr-8080-exec-9] - ==> Preparing: select * from picture WHERE picture_name like "%"?"%" LIMIT ?
DEBUG [http-apr-8080-exec-9] - ==> Parameters: 微信头像(String), 10(Integer)
DEBUG [http-apr-8080-exec-9] - <== Total: 1
可见这样写直接将传来的条件作为一个String参数拼接到SQL语句中,这样就可以避免SQL注入的问题了。
CONCAT
like CONCAT (CONCAT('%', #{name}, '%'))
<where>
<if test="pictureName != null and pictureName != ''">
picture_name like CONCAT(CONCAT('%', #{pictureName}, '%'))
</if>
</where>
DEBUG [http-apr-8080-exec-1] - ==> Preparing: SELECT count(0) FROM picture WHERE picture_name LIKE CONCAT(CONCAT('%', ?, '%'))
DEBUG [http-apr-8080-exec-1] - ==> Parameters: 微信头像(String)
DEBUG [http-apr-8080-exec-1] - <== Total: 1
这两种方式都是直接从SQL语句中着手,避免了SQL注入。
public class TestSQL {
public static boolean sql_inj(String str)
{
String inj_str = "'|and|exec|insert|select|delete|update|count|*|%|chr|mid|master|truncate|char|declare|;|or|-|+|,";
String inj_stra[] = inj_str.split("|");
for (int i = 0; i < inj_stra.length; i++)
{
if (str.indexOf(inj_stra[i]) != 0)
{
return true;
}
}
return false;
}
}