前言
SQL注入漏洞时WEB层面最高危的漏洞之一。在2005年前后,SQL注入漏洞随处可见,在用户登陆或者搜索时,只需输入一个单引号就可以检测出这类的漏洞。随着WEB应用程序的安全性不断提高,SQL注入漏洞越来越少,但是也变得越来越难以检测和预防。
作者学习书籍为张炳帅先生编著的《Web安全深度剖析》,随学习进度会更新其它知识。以下为文章正文部分,也可以认为是作者的学习记录。读者可以以此作为学习的参考,如有疑惑或者问题都可以在评论区或私信我。欢迎讨论。
一、SQL注入原理
要想更好的研究SQL注入原理,就必须深入了解每种数据库的SQL语法及特性。虽然大多数数据库都会遵循SQL标准,但是每个数据库也会有自己的特性和单行函数。
下列通过一个PHP万能登陆密码来解释SQL注入原理。本次环境为PHP+Mysql(phpstudy)
首先新建一个index.html文件,包含一个表单。如下图。
代码如下(示例):
<html>
<head>
<title>sql注入漏洞</title>
<meta charset="utf-8">
</head>
<body>
<form method="GET" action="php_mysql.php">
<table>
<tr>
<td>账号:</td>
<td><input type="text" width="50" name="user"></td>
</tr>
<tr>
<td>密码:</td>
<td><input type="password" width="50" name="pwd"></td>
</tr>
<tr>
<td><input type="submit" value="提交"></td>
</tr>
</table>
</form>
</body>
</html>
随后建立一个php_mysql.php文件,里面接受从表单中发送过来的数据并且连接数据库进行查询
代码如下(示例):
<?php
$servername = "localhost";
$dbusername = "Rock";
$dbpassword = "liu110120";
$dbname = "rock";
$user=$_GET['user']; //未经过滤
$pwd = $_GET['pwd'];
print($user."<br>");
$conn=mysqli_connect($servername,$dbusername,$dbpassword,$dbname) or die ("数据库连接失败");
$sql = "SELECT * FROM admins WHERE user='".$user."' and pwd ='".$pwd."'";
print($sql."<br>");
$result = mysqli_query($conn,$sql);
if($result){$row = mysqli_fetch_array($result);
print_r($row);}
else{
print("无查询结果");
}
?>
做好准备工作之后我们进行SQL注入。我们假设我们所在的网站中存在SQL注入漏洞(如何判断一个网站是否存在该漏洞,我会在SQL注入漏洞的分类一节解释)
这是正常登陆的界面
然后我们再使用一个特殊的用户 “ 'or 1 – ”,密码可随意写也可不写。点击提交之后发现也可以正常的从数据库获取数据。
但是查看数据库后发现里面并没有该用户,这是怎么回事呢?我们仔细查看PHP代码,发现它直接接受了来自用户的参数,并没有进行过滤。而数据库的执行语句也是你给他什么条件他就给你什么结果,也没有相应的过滤机制。当参数和查询语句合在一起时,查询语句就变成了SELECT * FROM admins WHERE user=' 'or 1 -- ' and pwd =''
。我们知道 1 为真,所以user =‘’ or 1这句代码就成了永真句。而且利用 “ --”注释掉了后面的语句,简化以下查询语句其实就变成了SELECT * FROM admins
。
注意:MySQL注释符 “- -”需要在后面加一个空格。也就是- -空格。。。
很显然,我们就这样构造了一个简单的SQL注入语句。如果仅仅是泄漏数据库的数据,这样的危害还是很小的。但是如果攻击者将注入语句变成 'or 1;drop table admins --
后果可想而知。
二、SQL注入漏洞分类
常见的漏洞分类有数字型和字符型,也有其它文章将其分的更细(例如post注入,Cookies注入,盲注等)。但是最根本的还是上述两种大类,其他的也只是它们的不同展示形式或者不同的注入位置。
1.数字型注入
当输入的参数为整型时,例如:ID,年龄,页码等,如果存在注入漏洞,可认为是数字型注入漏洞。
假设如果有URL为HTTP://domain/test.php/?id=1,我们要检测该网站是否存在注入漏洞时可以进行以下步骤:
第一步:猜测SQL查询语句为 select * from table where id = 1
第二步:测试语句select * from table where id = 1'
这样的语句肯定会出错,导致程序无法从数据库中获得数据,从而使原来的页面出现异常。
第三步:测试语句select * from table where id = 1 and 1 = 1
语句执行正常,返回的数据也和正常情况的一摸一样。
第四步:测试语句select * from table where id = 1 and 1 = 2
语句执行正常,但是无法获得与正常情况下一样的数据。因为1 = 2是永假式,所以返回的数据也不一样。
如果以上后三步都满足的话,则程序就很有可能会存在SQL数字注入漏洞。
总结:这种数字型注入漏洞最多出现在一些弱语言中例如PHP,ASP等。因为这些语言会自动推导参数的数据类型,而对于java等强类型语言,如果试图将一个int类型的数据转化为string类型,那么就会抛出错误。所以在防护数字型SQL注入漏洞这一领域,java,C#等强类型语言有着天然的优势。
2.字符型注入
数字型注入和字符型注入的最大区别就是字符型注入需要闭合单引号,而数字型注入不需要。所以字符型注入最关键的地方就在于如果闭合SQL语句和注释掉多余的代码。
字符型例句如下:
select * from table where id = 'admin'
当攻击者进行SQL注入时,如果输入的是“admin or 1 = 1”,则无法进行注入。因为该参数会被数据库当最查询的字符串,SQL语句就会变成:
select * from table where id = 'admin or 1 = 1'
如果输入的参数变成“admin ’ or 1 = 1 – ”,就可以进行注入,SQL语句如下:
select * from table where id = 'admin' or 1 = 1 -- '
不管是INSERT还是UPDATE语句都必须闭合单引号和注释掉多余的代码。
3.其它分类
POST注入:注入字段在POST数据中;
Cookies注入:注入字段在Cookies数据中;
延时注入:使用数据库延时特性进行注入;
搜索注入:注入处为搜索的位置;
base64注入:注入字符串需要经过base64加密
总结
以上简单的解释了SQL注入的原理,其实不管SQL注入漏洞的分类如何,攻击者的目的只有一个,就是绕过程序限制,使用户输入的数据带入数据库执行,利用数据库的特殊性获取更多的信息和更大的权限。
希望各位能从本文中学到新的知识。如果作者有哪些地方写的不对,可以私信或者在下方评论区指正。
参考书籍:张炳帅先生编著的《Web安全深入剖析》