【SQL】SQL注入是什么?如何防止SQL注入?

今天在优化一个查询的时候,关于一个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注入,就是通过把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语句的时候就应该试着去避免这些坑。

设计SQL时,应避免SQL注入

对于上面的例子中,我们改如何改进呢?

  1. 换一种方式实现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注入的问题了。

  1. 使用SQL中的字符串拼接函数CONCAT
    可将like语句写成: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注入。

  1. 除了直接优化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;
	}
}

你可能感兴趣的:(【总结】,【MySQL】)