SQL 注入及预防

1、什么是 sql 注入

SQL 注入(英语:SQL injection),是发生于应用程序与数据库层的安全漏洞。
当我们访问动态网页时, Web 服务器会向数据访问层发起 Sql 查询请求,如果权限验证通过就会执行 Sql 语句。

这种网站内部直接发送的 sql 请求一般不会有危险,但实际情况是很多时候需要结合用户的输入数据动态构造 Sql 语句,如果用户输入的参数被注入了 Sql 代码,Web 应用又未对动态构造的 Sql 语句使用的用户输入参数进行审查,那么这些注入进去的恶意 sql 就会被数据库服务器误认为是正常的 SQL 指令而运行,因此遭到破坏或是入侵。

2、sql 注入类型

(1)数字类型的注入

当输入参数为数字类型时,例如页码,ID等,存在注入时则为数字类型的注入。 测试方法如下:

http://www.xx.com/a.php?id=1 http://www.xx.com/a.php?id=1// 返回异常 
http://www.xx.com/a.php?id=1 and 1 =1              // 返回正常
http://www.xx.com/a.php?id=1 and 1 =2              // 返回异常

说明存在注入可能。
这类注入方式常见于PHP和ASP等弱类型语言中,弱类型语言会自动推导数据类型,例,输入的ID的值为1时,会自动推导出ID的数据类型为int型,当输入的ID的值为1 and 1 = 2时,会自动推导出ID的数据类型为string型,但JAVA或者C#这类强类型语言并没有这样的特性,int型强制转换成string型时,会抛出异常,注入失败,所以这类注入方式常见于弱类型语言中。

(2)字串类型的注入

当输入参数为字串类型时,则为字串类型的输入,其与数字类型的注入的区别在于:注入时需要使用单引号来闭合。
测试方法如下:

http://host/test.php?name=man' and '1'='1        // 返回成功
http://host/test.php?name=man' and '1'='2        // 返回失败 

这里就使上面的数字型变为了字符型。 假设我们网站的SQL语句是这样的:

SELECT * FROM news WHERE name='$name'

当我们构造输入为:man’ and ‘1’='1 语句就变成了:

SELECT * FROM news WHERE name='man' and '1'='1' 

可以发现:这个 SQL 已经闭合了。

注意:字符串注入时需要单引号半闭合,web 后台可以自动将 SQL 闭合

(3)搜索型注入点

假设我们的SQL查询语句是这样的:

SELECT * FROM news WHERE keyword like '%$keyword%' 

这里的 $keyword 是用户的输入。

当我们输入以下语句:pt%’ and 1=1 and ‘%’=’ 最终我们得到的语句是这样的:

SELECT * FROM news WHERE keyword like '%pt%' and 1=1 and '%'='%'

这个语句又一次的闭合了。

(4)内联式SQL注入

内联注入是指查询注入SQL代码后,原来的查询仍然全部执行。
假设我们的网站SQL查询语句是这样的:

SELECT * FROM admin WHER username='$name' AND password ='$passwd' 

这一看就是个登录页面的代码。
假如我们将输入语句 ’ or ‘’=’ 提交到登录框中的 username 或者提交到 password 框里面,这两种提交方法是不一样的,我们下面就来分析一下这两个提交方法。
1)提交到 username 我们的语句就会成为这样:

// fuzz是我们随便输入的字符串 
SELECT * FROM admin WHER username='' or ''='' AND password ='fuzz' 

数据库是不会存在username为NULL的字段的,所以第一句返回的是失败,第三句中,因为password是我们随便输入的,99.99%是不会存在这个密码的,于是AND之后,我们的第三句也是失败的,所以整个语句返回失败的。

2)提交到 password 则会是这样的:

// fuzz是我们随便输入的字符串 
SELECT * FROM admin WHER username='fuzz' AND password ='' or ''='' 

这里 username=‘fuzz’ 是返回失败的,但是’’=’'是返回成功的,OR逻辑是有一个是成功就返回成功,于是我们的整个语句就会返回成功。
返回成功之后我们就会绕过登录表单直接登录系统了

(5)终止式SQL注入

终止式SQL语句注入是指攻击者在注入SQL代码时,通过注释剩下的查询来成功结束该语句,于是被注释的查询不会被执行。我们还是拿(4)那个例子举例:

我们上面已经知道,在 username 框内填入 ’ or ‘’=’ 程序是不会返回成功的,我们就没有办法在 username 做文章了吗? 错了,我们还有终止式。还是上面那个SQL查询语句:

SELECT * FROM admin WHER username='$name' AND password ='$passwd'

这里我们构造如下 username 输入 ' or ''='' --

之后我们就可以得到如下的查询语句:

//  fuzz是我们随便输入的,--是注释符 
SELECT * FROM admin WHER username='' or ''='' --' AND password ='fuzz' 

username=’’ 肯定是返回失败的,但是’’=’'会返回成功,而其后面的 SQL 语句已经被我们注释掉了,是不会执行的,所以我们还是可以通过在username做这个手脚来绕过登录。

下面是我们常见的一些终止方式

终止字符串:
– , #, %23, %00, /*
终止方法:
– , ‘-- , ‘)-- , ) – , ‘)) --, ))–

3、sql 注入示例

(1)初步注入–绕过验证,直接登录

公司网站登陆框如下:
在这里插入图片描述
可以看到除了账号密码之外,还有一个公司名的输入框,根据输入框的形式不难推出SQL的写法如下:

SELECT * From Table WHERE Name=’XX’ andPassword=’YY’ and Corp=’ZZ’ 

我发现前两者都做一些检查,而第三个输入框却疏忽了,漏洞就在这里!注入开始,在输入框中输入以下内容:
在这里插入图片描述
用户名乱填,密码留空,这种情况下点击登录按钮后竟然成功登录了。我们看一下最终的SQL就会找到原因:

SELECT * FromTable WHERE Name=SQL inject’ and Password=and Corp=or 1=1–’ 

从代码可以看出,前一半单引号被闭合,后一半单引号被 “–”给注释掉,中间多了一个永远成立的条件“1=1”,这就造成任何字符都能成功登录的结果。而Sql注入的危害却不仅仅是匿名登录。

(2)修改数据库信息

网站提供了一个页面,让用户修改自己的昵称、邮件、地址、电话和密码。
在这里插入图片描述 更新个人信息的代码是这样写的 如何才能修改自己的 salary 金钱呢?

$conn = getDB(); 
$sql = "UPDATE credential SET nickname='$nickname',email='$email',address='$address',phonenumber='$phonenumber',Password='$pwd' WHERE id= '$input_id' "; 
$conn->query($sql)) 

咱们在命令行查看数据库的时候能够看到 Salary 信息,这样就可以利用漏洞修改Salary 。
漏洞:

$sql = "UPDATE credential SET nickname='$nickname',... 

用户输入的攻击串:

',salary='9999999' where Name='Alice'# 

这样数据库就错误的把用户输入的恶意 sql 误认为是正常的 SQL 指令而运行。

4、sql 注入的预防

(1)检查变量数据类型和格式

始终通过测试类型、长度、格式和范围来验证用户输入。
如果你的SQL语句是类似where id={$id} 这种形式,数据库里所有的id都是数字,那么就应该在SQL被执行前,检查确保变量id是int类型;如果是接受邮箱,那就应该检查并严格确保变量一定是邮箱的格式,其他的类型比如日期、时间等也是一个道理。总结起来:只要是有固定格式的变量,在SQL语句执行前,应该严格按照固定格式去检查,确保变量是我们预想的格式,这样很大程度上可以避免SQL注入攻击。

(2)测试输入的大小和数据类型,强制执行适当的限制

测试字符串变量的内容,只接受所需的值。拒绝包含二进制数据、转义序列和注释字符的输入内容。这有助于防止脚本注入,防止某些缓冲区溢出攻击。
使用 XML 文档时,根据数据的架构对输入的所有数据进行验证。
绝不直接使用用户输入内容来生成 Transact-SQL 语句。

(3)PreparedStatement:绑定变量,使用预编译语句 
MySQL的mysqli驱动提供了预编译语句的支持,不同的程序语言,都分别有使用预编译语句的方法
实际上,绑定变量使用预编译语句是预防SQL注入的最佳方式,使用预编译的SQL语句语义不会发生改变,在SQL语句中,变量用问号?表示,这样就无法改变SQL语句的结构。

sql 注入只对 sql 语句的编译过程有破坏作用,而 PreparedStatement 在接收用户输入前已经编译好了,执行阶段只是把输入串作为数据处理,而不再对 sql 语句进行解析编译,因此也就避免了 sql 注入问题
PrepareStatement 可以防止sql注入是采用了预编译的方法,先将SQL语句中可被客户端控制的参数集进行编译,生成对应的临时变量集,再使用对应的设置方法,为临时变量集里面的元素进行赋值,赋值函数setString(),会对传入的参数进行强制类型检查和安全检查,所以就避免了SQL注入的产生。
参数化能防注入的原因在于:语句是语句,参数是参数,参数的值并不会成为语句的一部分,数据库只按语句的语义执行而不受参数影响

5、JDBC 解决 sql 注入的问题

要从获取 Statement 说起:PreparedStatement 利用预编译的机制将 sql 语句的主干和参数分别传输给数据库服务器,从而使数据库分辨的出哪些是 sql 语句的主干哪些是参数,这样一来即使参数中带了 sql 的关键字,数据库服务器也仅仅将他当作参数值使用,从而从原理上防止了 sql 注入的问题

6、关于安全性

(1)SQL 注入不仅能通过输入框,还能通过 URL 达到目的

(2) 除了服务器错误页面,还有其他办法获取到数据库信息。

(3)可通过软件模拟注入行为,这种方式盗取信息的速度要比你想象中快的多。

(4)漏洞跟语言平台无关,并非asp才有注入漏洞而asp.net就没有注入漏洞,一切要看设计者是否用心。

(5)对用户输入的内容要时刻保持警惕。

(6)只有客户端的验证等于没有验证

(7)永远不要把服务器错误信息暴露给用户

おすすめ

転載: blog.csdn.net/IT__learning/article/details/119948514
おすすめ