sql注入原理及防范

前言

        sql注入是一种危险系数较高的攻击方式,如今因为咱们持久层框架愈来愈多,大部分框架会处理这个问题,所以致使咱们对它的关注度愈来愈少了。最近部门在整理安全漏洞时,提到了一些关于sql注入的修改点,所以共同记录学习一下。java

正文

原理

        sql注入的原理是将sql代码假装到输入参数中,传递到服务器解析并执行的一种攻击手法。也就是说,在一些对server端发起的请求参数中植入一些sql代码,server端在执行sql操做时,会拼接对应参数,同时也将一些sql注入攻击的“sql”拼接起来,致使会执行一些预期以外的操做。mysql

示例:

        好比咱们使用的登陆接口:在登陆界面包括用户名和密码输入框,以及提交按钮,输入用户名和密码,提交。sql

        登陆时调用接口/user/login/ 加上参数username、password,首先链接数据库,而后后台对请求参数中携带的用户名、密码进行参数校验,即sql的查询过程。假设正确的用户名和密码为ls和123456,输入正确的用户名和密码、提交,至关于调用了如下的SQL语句。数据库

SELECT * FROM user WHERE username = 'ls' AND password = '123456'
复制代码

        sql中会将#及--之后的字符串当作注释处理,若是咱们使用“' or 1=1 #” 做为用户名参数,那么服务端构建的sql语句就以下:安全

select * from users where username='' or 1=1#' and password='123456'
复制代码

        而#会忽略后面的语句,所以上面的sql也等价于:bash

select * from users where username='' or 1=1
复制代码

        而1=1属于常等型条件,所以这个sql便成为了以下,查询出全部的登录用户。服务器

select * from users
复制代码

        其实上面的sql注入只是在参数层面作了些手脚,若是是引入了一些功能性的sql那就更危险了,好比上面的登录接口,若是用户名使用这个“' or 1=1;delete * from users; #”,那么在";"以后至关因而另一条新的sql,这个sql是删除全表,是很是危险的操做,所以sql注入这种仍是须要特别注意的。mybatis

解决sql注入原理

sql预编译

        在知道了sql注入的原理以后,咱们一样也了解到mysql有预编译的功能,指的是在服务器启动时,mysql client把sql语句的模板(变量采用占位符进行占位)发送给mysql服务器,mysql服务器对sql语句的模板进行编译,编译以后根据语句的优化分析对相应的索引进行优化,在最终绑定参数时把相应的参数传送给mysql服务器,直接进行执行,节省了sql查询时间,以及mysql服务器的资源,达到一次编译、屡次执行的目的,除此以外,还能够防止SQL注入。app

        具体是怎样防止SQL注入的呢?实际上当将绑定的参数传到mysql服务器,mysql服务器对参数进行编译,即填充到相应的占位符的过程当中,作了转义操做。         咱们经常使用的jdbc就有预编译功能,不只提高性能,并且防止sql注入。框架

String sql = "select id, no from user where id=?";
        PreparedStatement ps = conn.prepareStatement(sql);
        ps.setInt(1, id);
        ps.executeQuery();
复制代码

严格的参数校验

        参数校验就没得说了,在一些不应有特殊字符的参数中提早进行特殊字符校验便可。

框架的支持——mybatis

        java生态中很经常使用的持久层框架mybatis就能很好的完成对sql注入的预防,以下两个mapper文件,前者就能够预防,然后者不行。

<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;

        所以若是不是真的要执行功能型的sql如删除表、建立表等,仍是须要用#来避免sql注入。mybatis底层仍是使用jdbc的预编译功能。

结语

        以上是对sql注入方式、原理及危害、问题解决作出的一些小小的总结,若是有问题但愿你们可以指出,多多交流。

相关文章
相关标签/搜索