前言

什么是SQL注入?来看一下下面的案例场景,这是正常情况下的登陆场景:

详解Web安全---SQL注入漏洞_php

而当我们使用用户名 ‘:–  的时候,密码随便输入也可以登陆成功!

详解Web安全---SQL注入漏洞_sql_02

这时候对比两条sql就能发现,其实用户通过在用户名写入的sql符号将内部sql提前结束,并且将后半句检索条件注释起来达到免密码登陆。

小结:SQL注入就是本来我只有我能操作数据库,本来只是让你输入内容就走,而你却输入命令,从而在我不知情下操作数据库。

手工检测

Web应用的主要注入点有:

  1. POST请求体中的参数;
  2. GET请求头URL中的参数;
  3. 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

永假,返回空

终止式注入

详解Web安全---SQL注入漏洞_sql_03

漏洞修复

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、框架技术

详解Web安全---SQL注入漏洞_sql语句_04

详解Web安全---SQL注入漏洞_sql_05

4、使用存储过程

详解Web安全---SQL注入漏洞_sql_06

去期待陌生,去拥抱惊喜。