SQL注射原理
所谓SQL注入,就是通过把SQL命令插入到Web表单提交或输入域名或页面请求的查询字符串,最终达到欺骗服务器执行恶意的SQL命令。具体来说,它是利用现有应用程序,将(恶意)的SQL命令注入到后台数据库引擎执行的能力,它可以通过在Web表单中输入(恶意)SQL语句得到一个存在安全漏洞的网站上的数据库,而不是按照设计者意图去执行SQL语句。
攻击方式
猜表名
And (Select count(*) from 表名)<>0
and exists (select * from 表名)
猜列名
And (Select count(列名) from 表名)<>0
and exists (select 列名 from 表名)
拉全表
' or 1 = 1 #
#可以注释掉后面的一行SQL代码
实战演练
正常请求
http://xxxxxx/sc?cn=sc
响应结果数量:
注入请求
http://xxxxxx/sc?cn=sc' or 1= 1 or '' = '
响应结果数量:
可以从响应结果数中看到,它已经几乎把我们所有的数据给拉了出来。
防御调整
原查询语句
<!--查询学校--> <select id= "selectSchool" resultMap= "BaseResultMap" parameterType= "map" >
select xxx from yyyy
where zzz = 'section' and deleted_at is null and is_open = 1
< if test= "name !=null" >
and name like '%${name}%'
</ if >
< if test= "pinyin !=null" >
and pinyin_initial like '${pinyin}%'
</ if >
order by id
</select> |
调整后语句
<!--查询学校--> <select id= "selectSchool" resultMap= "BaseResultMap" parameterType= "map" >
select xxx from yyyy
where zzz = 'section' and deleted_at is null and is_open = 1
< if test= "name !=null" >
and name like concat(concat( '%' ,#{name}), '%' )
</ if >
< if test= "pinyin !=null" >
and pinyin_initial like concat((#{pinyin}), '%' )
</ if >
order by id
</select> |
调整后执行日志
2016 - 03 - 11 16 : 06 : 29.661 [http-nio- 8080 -exec- 8 ] DEBUG org.mybatis.spring.SqlSessionUtils 106 getSqlSession- Creating a new SqlSession
2016 - 03 - 11 16 : 06 : 29.663 [http-nio- 8080 -exec- 8 ] DEBUG org.mybatis.spring.SqlSessionUtils 142 getSqlSession- SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession @647a5210 ] was not registered for synchronization because synchronization is not active
2016 - 03 - 11 16 : 06 : 29.677 [http-nio- 8080 -exec- 8 ] DEBUG o.m.spring.transaction.SpringManagedTransaction 85 openConnection- JDBC Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl @69787b57 ] will not be managed by Spring
2016 - 03 - 11 16 : 06 : 29.678 [http-nio- 8080 -exec- 8 ] DEBUG c.z.customer.base.dao.DistrictMapper.selectSchool 132 debug- ooo Using Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl @69787b57 ]
2016 - 03 - 11 16 : 06 : 29.678 [kafka-producer-network-thread | producer- 2 ] DEBUG org.apache.kafka.clients.NetworkClient 385 maybeUpdateMetadata- Trying to send metadata request to node - 1
2016 - 03 - 11 16 : 06 : 29.679 [http-nio- 8080 -exec- 8 ] DEBUG c.z.customer.base.dao.DistrictMapper.selectSchool 132 debug- ==> Preparing: select id ,name ,parent_id,district_type,pinyin,pinyin_initial from districts where district_type = 'section' and deleted_at is null and is_open = 1 and name like concat(concat( '%' ,?), '%' ) order by id
2016 - 03 - 11 16 : 06 : 29.682 [http-nio- 8080 -exec- 8 ] DEBUG c.z.customer.base.dao.DistrictMapper.selectSchool 132 debug- ==> Parameters: sh ' or 1 =1 or ' ' = ' (String)
2016 - 03 - 11 16 : 06 : 29.705 [http-nio- 8080 -exec- 8 ] DEBUG org.mybatis.spring.SqlSessionUtils 170 closeSqlSession- Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession @647a5210 ]
|
调整后响应数据为空
{
"success"
:
true
,
"data"
: {
"schools"
: []
}
}
#号与$区别
#号表示参数,$代表一个字符串。
select a,b,c from table1 where id=#value#
传入参数后如:value=
"1"
,
则可生成:select a,b,c from table1 where id=‘
1
’。
select a,b,c from table1 where city like
'%$value$%'
传入参数后:value=
"berg"
,
则可生成:select a,b,c from table1 where city like
'%berg%'
。
${} 为原样输出,你传什么,sql里就填入什么。比如有引号它也会原样填到sql里。
#{} 会使用 PreparedStatement,变量处用 ? 代替。 在能使用 #{} 尽量使用它吧,可以防止sql注入。
在用#时,会在数据库里面生成一颗执行树,每一个参数就像是填空题一样被放入这颗执行树中。每次SQL执行时,执行树都是生成好的,不用重复解析SQL。同时也提升了SQL执行效率。