1、什么是SQL注入
SQL 注入(SQL Injection)是发生在 Web 程序中数据库层的安全漏洞,是网站存在最多也是最简单的漏洞。攻击者在 事先定义好的 SQL 语句中添加额外的 SQL 语句,在管理员不知情的情况下实现非法操作,以此来实现欺骗数据库服务器执行非授权的任意查询,从而进一步获取到数据信息
2、产生SQL注入的原因
程序对用户输入数据的合法性没有判断和处理
攻击者在用户输入的字符串中加入 SQL 语句,如果在设计不良的程序中忽略了检查,那么这些注入进去的 SQL 语句就会被数据库服务器误认为是正常的 SQL 语句而运行,攻击者就可以执行计划外的命令或访问未被授权的数据。
3、产生SQL注入需要的条件
哪里可能存在SQL注入?
GET
POST
HTTP头部注入
Cookie注入
即满足传递给后端的参数可以受用户控制且参数内容会直接被带到数据库语句中查询
4、SQL注入的原理
1)恶意拼接SQL语句
当我们在页面输入用户账号时,后台的数据库可能执行形似 select * from 表名 where username=$user_id 的SQL语句,此时如果我们输入的user_id后拼接上;DELETE FROM users;将会实现将users表中的数据删除,类似地通过拼接其它SQL语句也可以执行相应的功能
2)添加额外条件
当我们在需要账号密码进行登录时,如果在账号密码输入处添加额外的恒成立的条件,即可在没有正确的账号密码时仍让操作请求成功。因为在验证账号密码时,后台的数据库可能执行形似 "select * from 表名 where username='" + username + "'and password='" + password + "'"; 的SQL语句,如果在账号和密码后都添加or 1=1,由于or左右两端的条件只需有一个为真则结果为真,而1=1恒为真,所以在where语句进行判断时账号和密码处都为成真赋值,这将实现绕过账号密码登录
5、SQL注入的分类
1)根据数据类型分类
整形注入
字符串型注入
判断方法
and 1=1 / and 1=2 回显页面不同(整形判断)
输入and 1=1 和 and 1=2后发现页面变化,判断为整形注入 (因为如果为字符型判断时会变成’id=1 and 1=1‘以及’id=1 and 1=2’显然无法发挥and的作用,故结果都应该为false,页面不变)
单引号判断 ‘ 显示数据库错误信息或者页面回显不同(整形,字符串类型判断)
-1/+1 回显下一个或上一个页面(整型判断)
2)根据注入的语法分类
UNION query SQL injection(可联合查询注入):可以使用union的情况下的注入。
Error-based SQL injection(报错型注入):即页面会返回错误信息,或者把注入的语句的结果直接返回在页面中。
Boolean-based blind SQL injection(布尔型注入):即可以根据返回页面判断条件真假的注入。
Time-based blind SQL injection(基于时间延迟注入):即不能根据页面返回内容判断任何信息,用条件语句查看时间延迟语句是否执行(即页面返回时间是否增加)来判断。
Stacked queries SQL injection(可多语句查询注入):可以同时执行多条语句的执行时的注入。
6、防止SQL注入
1)检查变量数据类型和格式
只要是有固定格式的变量,在SQL语句执行前,应该严格按照固定格式去检查,确保变量是我们预想的格式,这样很大程度上可以避免SQL注入攻击。
2)过滤特殊符号
对于无法确定固定格式的变量,对特殊符号进行过滤或转义处理。
3)绑定变量,使用预编译语句
预防SQL注入的最佳方式,使用预编译的SQL语句语义不会发生改变,在SQL语句中,变量用问号?表示
什么是预编译语句
所谓预编译语句就是将这类语句中的值用占位符替代,可以视为将sql语句模板化或者说参数化(因为一条sql语句可能会反复执行,而只变化部分值),一般称这类语句叫Prepared Statements或者Parameterized Statements.
7、PrepareStatement可以防止sql注入的原因
原理是采用了预编译的方法,先将SQL语句中可被客户端控制的参数集进行编译,生成对应的临时变量集,再使用对应的设置方法,为临时变量集里面的元素进行赋值,赋值函数setString(),会对传入的参数进行强制类型检查和安全检查,所以就避免了SQL注入的产生。
1)为什么Statement会被sql注入
因为Statement没有实现参数化,传入的参数可能会改变SQL语句的结构,如语句
"select * from 表名 where username='" + username + "'and password='" + password + "'";
意为查找出用户名等于username且密码等于password的数据
当用户在username处输入1' or 1=1 #时,语句将变为
"select * from 表名 where username='1' or 1=1 # and password = '1' or 1=1;
意为无论数据的用户名和密码是什么(因为或语句使得判断为永真式)(#将后面语句注释,解决引号闭合问题,未必需要),都将该条数据输出
这显然改变了原SQL语句的目的
2)为什么PrepareStatement可以防止sql注入
因为PrepareStatement实现参数化,其格式为:
select*from tablename where username=? and password=?
显然格式固定,将参数与语句分开,不将参数直接融入语句,而是先在外部判断是否合法,因此不会因为传入的参数特殊而改变语句的原作用
where username=? and password=?`
显然格式固定,将参数与语句分开,不将参数直接融入语句,而是先在外部判断是否合法,因此不会因为传入的参数特殊而改变语句的原作用