🍅 Java学习路线配套文章:搬砖工逆袭Java架构师
🍅 Java经典面试题大全:10万字208道Java经典面试题总结(附答案)
🍅 简介:Java领域优质创作者🏆、CSDN哪吒公众号作者✌ 、Java架构师奋斗者💪
🍅 扫描主页左侧二维码,加入群聊,一起学习、一起进步
🍅 欢迎点赞 👍 收藏 ⭐留言 📝
目录
一、什么是sql注入
二、sql注入攻击的总体思路
三、sql注入攻击实例
四、如何防御sql注入
1、检查变量数据类型和格式
2、过滤特殊符号
3、绑定变量,使用预编译语句
五、预编译
(一)预编译是什么
1、执行sql一般分为三步:
2、PreparedStatement
(二)预编译的sql语句处理
(三)预编译语句的作用
1、提高效率
2、提高安全性
(四)预编译语句的使用
1、创建PreparedStatement对象
2、传递IN参数
(五)为什么PreparedStatement可以防止sql注入
六、mybatis与sql注入
(一)mybatis中#和$的区别
(二)mybatis是如何防止sql注入的
一、什么是sql注入
sql注入是比较常见的网络攻击方式之一,它不是利用操作系统的bug来实现攻击,而是针对程序员编写时的疏忽,通过sql语句,实现无账号登录,甚至篡改数据库。
二、sql注入攻击的总体思路
1、寻找sql注入的位置
2、判断服务器类型和后台数据库类型
3、针对不同的服务器和数据库特点进行sql注入攻击
三、sql注入攻击实例
四、如何防御sql注入
注意:但凡是sql注入漏洞的程序,都是因为程序要接受来自客户端用户输入的变量或URL传递的参数,并且这个变量或参数是组成sql语句的一部分,对于用户输入的内容或传递的参数,我们应该要时刻保持警惕,这是安全领域里的【外部数据不可信任】的原则,纵观web安全领域的各种攻击方式,大多数都是因为开发者违反了这个原则而导致的,所以自然能想到,就是变量的检测、过滤、验证下手,确保变量是开发者所预想的。
1、检查变量数据类型和格式
数据类型检查,sql执行前,要进行数据类型检查,如果是邮箱,参数就必须是邮箱的格式,如果是日期,就必须是日期格式;
只要是有固定格式的变量,在SQL语句执行前,应该严格按照固定格式去检查,确保变量是我们预想的格式,这样很大程度上可以避免SQL注入攻击。
如果上述例子中id是int型的,效果会怎样呢?无法注入,因为输入注入参数会失败。比如上述中的name字段,我们应该在用户注册的时候,就确定一个用户名规则,比如5-20个字符,只能由大小写字母、数字以及汉字组成,不包含特殊字符。此时我们应该有一个函数来完成统一的用户名检查。不过,仍然有很多场景并不能用到这个方法,比如写博客,评论系统,弹幕系统,必须允许用户可以提交任意形式的字符才行,否则用户体验感太差了。
2、过滤特殊符号
3、绑定变量,使用预编译语句
五、预编译
(一)预编译是什么
1、执行sql一般分为三步:
- 语法和语义解析
- 优化sql语句,制定执行计划
- 执行sql,返回结果
2、PreparedStatement
很多情况下,我们的一条sql语句可能被反复执行,或每次执行的时候只有个别的值不同,比如query的where子句值不同,update的set子句值不同,insert的value值不同。
如果每次都需要经过上面的语法语义解析、语句优化、制定执行计划等操作,效率显而易见,很低。
所谓预编译就是将这些语句中的值用占位符替代,可以视为将sql语句模板化或者参数化,一般称这类语句叫PreparedStatement。
预编译语句的优势在于:一次编译、多次执行,省去了解析优化等繁琐过程,此外预编译语句能防止sql注入。
(二)预编译的sql语句处理
预编译语句PreparedStatement是java.sql中的一个接口,它是Statement的子接口。通过Statement对象执行sql语句时,需要将sql语句发送给DBMS,由DBMS首先进行编译后再执行。预编译语句和Statement不同,在创建PreparedStatement对象时就指定了sql语句,该语句立即发送给DBMS进行编译。当该编译语句被执行时,DBMS直接运行编译后的sql语句,而不需要像其它sql语句那样首先将其编译。预编译的sql语句处理性能稍微高于普通的传递变量的方法。
数据库管理系统(Database Management System)是一种操纵和管理数据库的大型软件,用于建立、使用和维护数据库,简称DBMS。它对数据库进行统一的管理和控制,以保证数据库的安全性和完整性。用户通过DBMS访问数据库中的数据,数据库管理员也通过DBMS进行数据库的维护工作。它可以支持多个应用程序和用户去建立,修改和访问数据库。大部分DBMS提供数据定义语言DDL和数据操作语言DML,供用户定义数据库的模式结构与权限约束,实现对数据的追加、删除等操作。
(三)预编译语句的作用
1、提高效率
当需要对数据库进行数据插入、更新或删除的时候,程序会发送整个sql语句给数据库处理和执行。数据库处理一个sql语句,需要完成解析sql语句、检查语法和语义以及生产代码,处理时间要比执行语句所需要的时间长。预编译语句在创建的时候就已经是将指定的sql语句发送给了DBMS,完成了解析、检查、编译等工作。因此,当一个sql语句需要执行多次时,使用预编译语句可以减少处理时间,提高执行效率。
2、提高安全性
例如上面的sql注入攻击实例,登录验证,根本不用,直接进,加入又在后面追加上drop table student;我靠,防不胜防,数据表直接没了,很多数据库是不会成功的,但也有很多数据库可以使用这些语句执行,而如果使用预编译语句,传入的任何内容就不会和原来的语句发生任何匹配的关系。只要全使用预编译语句,就用不着对传入的数据做任何过虑,而如果使用普通的statement,有可能要对drop,等做费尽心机的判断和过虑。
(四)预编译语句的使用
1、创建PreparedStatement对象
String sql = "insert into studentvalues(?,?,?,?)";//组织一条含有参数的SQL语句
PreparedStatement ps = conn.prepareStatement(sql);
insert into student values(?,?,?,?);//它已发送给DBMS,并为执行作好了准备。
2、传递IN参数
在执行 PreparedStatement 对象之前,必须设置每个 ? 参数的值。这可通过调用 setXXX 方法来完成,其中 XXX 是与该参数相应的类型。
(五)为什么PreparedStatement可以防止sql注入
Preparement样式为
select * from student where id=? and name=?
该SQL语句会在得到用户的输入之前先用数据库进行预编译,这样的话不管用户输入什么id和用户名的判断始终都是并的逻辑关系,防止了SQL注入。
简单总结,参数化能防注入的原因在于,语句是语句,参数是参数,参数的值并不是语句的一部分,数据库只按语句的语义跑,至于跑的时候是带一个普通背包还是一个怪物,不会影响行进路线,无非跑的快点与慢点的区别。
六、mybatis与sql注入
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.guor.mapper.StudentMapper">
<select id="queryStudentByStuNo" parameterType="int" resultType="com.entity.Student">
select * from student where id = ${stuNo}
</select>
<insert id="addStudent" parameterType="com.entity.Student">
insert into student(id,name,age) values (#{id},#{name},#{age})
</insert>
</mapper>
(一)mybatis中#和$的区别
1、#将传入的数据都当成一个字符串,会对自动传入的数据加一个双引号。
如:where id=#{id},如果传入的值是1,那么解析成sql时的值为where id="1", 如果传入的值是id,则解析成的sql为where id="id"。
2、$将传入的数据直接显示生成在sql中。
如:where id=${id},如果传入的值是1,那么解析成sql时的值为where username=1;
如果传入的值是;drop table user;,则解析成的sql为:select * from student where id=1;drop table student;
3、#方式能够很大程序防止sql注入,$方式无法防止sql注入。
4、$方式一般用于传入数据库对象,比如表明。
5、一般能用#的就不要使用$,若不得不使用$,则要做好前期校验工作,防止sql注入攻击。
6、在mybatis中,涉及到动态表名和列名时,只能使用${xxx}这样的参数形式。所以这样的参数需要我们在代码中手工进行处理来防止注入。
(二)mybatis是如何防止sql注入的
MyBatis框架作为一款半自动化的持久层框架,其SQL语句都要我们自己手动编写,这个时候当然需要防止SQL注入。其实,MyBatis的SQL是一个具有“输入+输出”的功能,类似于函数的结构,参考上面的两个例子。其中,parameterType表示了输入的参数类型,resultType表示了输出的参数类型。回应上文,如果我们想防止SQL注入,理所当然地要在输入参数上下功夫。上面代码中使用#的即输入参数在SQL中拼接的部分,传入参数后,打印出执行的SQL语句,会看到SQL是这样的:
select * from student where id=? and name=?
不管输入什么参数,打印出的SQL都是这样的。这是因为MyBatis启用了预编译功能,在SQL执行前,会先将上面的SQL发送给数据库进行编译;执行时,直接使用编译好的SQL,替换占位符“?”就可以了。因为SQL注入只能对编译过程起作用,所以这样的方式就很好地避免了SQL注入的问题。
🍅 Java学习路线配套文章:搬砖工逆袭Java架构师
🍅 Java经典面试题大全:10万字208道Java经典面试题总结(附答案)
🍅 简介:Java领域优质创作者🏆、CSDN哪吒公众号作者✌ 、Java架构师奋斗者💪
🍅 扫描主页左侧二维码,加入群聊,一起学习、一起进步
🍅 欢迎点赞 👍 收藏 ⭐留言 📝
添加微信,备注1024,赠送Java学习路线思维导图