一文给你讲明白SQL注入原理

举报
JavaEdge 发表于 2021/06/03 23:05:29 2021/06/03
【摘要】 sql注入是一种网络攻击,持久层框架都会自己处理该问题,所以日常开发感觉不到,但是为了面试我们还是得熟悉。 原理 将sql代码伪装到输入参数,传递到服务器解析并执行的一种攻击手法。 即在一些对server端发起的请求参数中植入一些sql代码,server端在执行sql操作时,会拼接对应参数,同时也将一些sql注入攻击的“sql”拼接起来,导致会执行一些预期之外的操...

sql注入是一种网络攻击,持久层框架都会自己处理该问题,所以日常开发感觉不到,但是为了面试我们还是得熟悉。

原理

将sql代码伪装到输入参数,传递到服务器解析并执行的一种攻击手法。

即在一些对server端发起的请求参数中植入一些sql代码,server端在执行sql操作时,会拼接对应参数,同时也将一些sql注入攻击的“sql”拼接起来,导致会执行一些预期之外的操作。

案例

在登录界面包括用户名和密码输入框,以及提交按钮,输入用户名和密码,提交。
登录时调用接口/user/login/ 加上参数username、password,首先连接数据库,然后后台对请求参数中携带的用户名、密码进行参数校验,即sql的查询过程。假设正确的用户名和密码为ls和123456,输入正确的用户名和密码、提交,相当于调用了以下的SQL语句。

SELECT * FROM user WHERE username = 'ls' AND password = '123456'

  
 
  • 1

sql中会将#及–以后的字符串当做注释处理,如果我们使用“’ or 1=1 #” 作为用户名参数,那么服务端构建的sql语句就如下:

select * from users where username='' or 1=1#' and password='123456'

  
 
  • 1

而#会忽略后面的语句,因此上面的sql也等价于:

select * from users where username='' or 1=1

  
 
  • 1

而1=1属于常等型条件,因此这个sql便成为了如下,查询出所有的登陆用户。

select * from users

  
 
  • 1

其实上面的sql注入只是在参数层面做了些手脚,如果是引入了一些功能性的sql那就更危险了,比如上面的登陆接口,如果用户名使用这个“’ or 1=1;delete * from users; #”,那么在";"之后相当于是另外一条新的sql,这个sql是删除全表,是非常危险的操作,因此sql注入这种还是需要特别注意的。

解决sql注入

sql预编译

MySQL的预编译

在服务器启动时,mysql client把sql语句模板(变量采用占位符)发给mysql服务器,mysql服务器对sql语句模板进行编译,编译之后根据语句的优化分析对相应的索引进行优化,在最终绑定参数时把相应的参数传送给mysql服务器,直接执行,节省了sql查询时间,以及mysql服务器的资源,达到一次编译、多次执行的目的,除此之外,还可以防止SQL注入。

何时真正防止SQL注入

当将绑定的参数传到mysql服务器,mysql服务器对参数进行编译,即填充到相应的占位符的过程中,做了转义操作。
Java 的 jdbc就有预编译功能,不仅提升性能,而且防止sql注入。

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

  
 
  • 1
  • 2
  • 3
  • 4

严格的参数校验

在一些不该有特殊字符的参数中提前进行特殊字符校验即可。

框架支持-mybatis

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
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

#将传入的数据都当成一个字符串,会对自动传入的数据加一个双引号。
如: where username=#{username},如果传入的值是111,那么解析成sql时的值为where username="111", 如果传入的值是id,则解析成的sql为

where username="id"

  
 
  • 1

如果传入的值是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的预编译功能。

文章来源: javaedge.blog.csdn.net,作者:JavaEdge.,版权归原作者所有,如需转载,请联系作者。

原文链接:javaedge.blog.csdn.net/article/details/110551959

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。