前言
什么是SQL注入?来看一下下面的案例场景,这是正常情况下的登陆场景:
而当我们使用用户名 ‘:– 的时候,密码随便输入也可以登陆成功!
这时候对比两条sql就能发现,其实用户通过在用户名写入的sql符号将内部sql提前结束,并且将后半句检索条件注释起来达到免密码登陆。
小结:SQL注入就是本来我只有我能操作数据库,本来只是让你输入内容就走,而你却输入命令,从而在我不知情下操作数据库。
手工检测
Web应用的主要注入点有:
- POST请求体中的参数;
- GET请求头URL中的参数;
- Cookie。
闭合类型
1、数字型(%后面跟的是该字符的16进制编码,为了在http中传输特殊字符和汉字等而使用,其中%23表示'#',%20表示空格)
URL: http://localhost/index.php?id=1
SQL语句:SELECT * FROM users WHERE id=1 LIMIT 0,1
注入语句:http://localhost/index.php?id=1 and 1=1 %23
SQL语句:SELECT * FROM users WHERE id=1 and 1=1 # LIMIT 0,1
2、字符型
URL: http://localhost/index.php?id=1
SQL语句:SELECT * FROM users WHERE id='1' LIMIT 0,1
注入语句:http://localhost/index.php?id=1' and 1=1 %23
SQL语句:SELECT * FROM users WHERE id='1' and 1=1 #' LIMIT 0,1
3、其他变体
URL: http://localhost/index.php?id=1
SQL语句:SELECT * FROM users WHERE id=('1') LIMIT 0,1
注入语句:http://localhost/index.php?id=1') and 1=1 %23
SQL语句:SELECT * FROM users WHERE id=('1') and 1=1 #') LIMIT 0,1
字符型注入
测试字符串 | 变体 | 预期结果 |
’ | N/ A | 触发数据库返回错误 |
’ or ‘1’ = '1 | ') or (‘1’ = '1 | 永真,返回所有行 |
’ or ‘1’ = '2 | ') or (‘1’ = '2 | 空,不影响返回结果 |
’ and ‘1’ = '2 | ') and (‘1’ = '2 | 永假,返回空 |
如果系统限制了某些字符不能输入(前端限制某些字符不可输入至输入框内),我们可以对相关字符进行URL编码转换后尝试绕过,常见需要编码的字符如下:
- 加号(+)编码为:
%2B
- 等号(=)编码为:
%3D
- 单引号(’)编码为:
%27
- 注释号(#)编码为:
%23
数字型注入
测试字符串 | 变体 | 预期结果 |
’ | N/ A | 触发数据库返回错误 |
or 1 = 1 | ) or (1 = 1 | 永真,返回所有行 |
or 1 = 2 | ) or (1 = 2 | 空,不影响返回结果 |
and 1 = 2 | ) and (1 = 2 | 永假,返回空 |
终止式注入
漏洞修复
SQL注入,就是通过把SQL命令插入到Web表单提交或输入域名或页面请求的查询字符串,最终达到欺骗服务器执行恶意的SQL命令。
具体来说,它是利用现有应用程序,将(恶意的)SQL命令注入到后台数据库引擎执行的能力,它可以通过在Web表单中输入(恶意)SQL语句得到一个存在安全漏洞的网站上的数据库,而不是按照设计者意图去执行SQL语句。
1、预编译语句
会产生上面的情况是因为上面的SQL是使用动态拼接的方式,所以sql传入的方式可能改变sql的语义。
动态拼接就是在java中java变量和SQL语句混合使用:
select * from user where userName=’”+userName+”’ and password = ‘”+password”’
所以要使用 preparedStatement的参数化SQL,通过先确定语义,再传入参数(通过setInt,setString,setBoolean传入参数),就不会因为传入的参数改变sql的语义。
来看看参数化SQL使用案例:
/建立数据连接
conn=ds.getConnection();
//1.设置prepareStatement带占位符的sql语句
PreparedStatement ptmt = conn.prepareStatement("select * from user where userName = ? and password = ?");
//2.设置参数
ptmt.setString(1, "张三");
ptmt.setString(2, "123456");
rs=ptmt.executeQuery();
while(rs.next()){
System.out.println("登陆成功");
return;
}
System.out.println("登陆失败");
2、字符串过滤
比较通用的一个方法(||之间的参数可以根据自己程序的需要添加):
public static boolean sql_inj(String str) {
String inj_str = "'|and|exec|insert|select|delete|update|
count|*|%|chr|mid|master|truncate|char|declare|;|or|-|+|,";
String inj_stra[] = split(inj_str,"|");
for (int i=0 ; i < inj_stra.length ; i++ ){
if (str.indexOf(inj_stra[i])>=0){
return true;
}
}
return false;
}
3、框架技术
4、使用存储过程
去期待陌生,去拥抱惊喜。