7

一次团队内部比赛经历

 2 years ago
source link: https://qwzf.github.io/2019/04/26/%E4%B8%80%E6%AC%A1%E5%9B%A2%E9%98%9F%E5%86%85%E9%83%A8%E6%AF%94%E8%B5%9B%E7%BB%8F%E5%8E%86/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

经过这次登录框被多数人打爆的经历,我反思了很多。由于当时为了快点写好登录框,没有考虑对登录框的SQL注入进行预防。在写登录框制作总结时,也发现了自己登录框存在的漏洞,但是没有去改。。。最终导致这一惨剧的发生!!!

经过不断的修改,最终把我已知漏洞修复完毕。在这个过程中,我收获了很多,所以总结了一下。。

SQL注入介绍

所谓SQL注入式攻击,就是输入域或页面请求的查询字符串,欺骗服务器执行恶意的SQL命令。在某些表单中,用户输入的内容直接用来构造(或者影响)动态SQL命令(也就是所谓构造payload),或作为存储过程的输入参数,这类表单特别容易受到SQL注入式攻击

SQL注入发生

当应用程序使用输入内容来构造payload以访问数据库时,会发生sql注入攻击。

如果代码使用存储过程,而这些存储过程作为包含未筛选的用户输入的 字符串来传递,也会发生sql注入。

sql注入可能导致攻击者使用应用程序登陆在数据库中执行命令。如果应用程序使用特权过高的帐户连接到数据库,这种问题会变得很严重。在某些表单中,用户输入的内容直接用来构造payload,或者作为存储过程的输入参数,这些表单特别容易受到sql注入的攻击。而许多 网站程序在编写时,没有对用户输入的合法性进行判断或者程序中本身的变量处理不当,使应用程序存在安全隐患。这样,用户就可以提交一段数据库查询的代码, 根据程序返回的结果,获得一些敏感的信息或者控制整个服务器,于是sql注入就发生了。

SQL注入的预防

1.永远不要信任用户的输入。对用户的输入进行校验,可以通过正则表达式,或限制长度;对单引号和双”-“进行转换等。

2.永远不要使用动态拼装sql,可以使用参数化的sql或者直接使用存储过程进行数据查询存取。

3.永远不要使用管理员权限的数据库连接,为每个应用使用单独的权限有限的数据库连接。

4.不要把机密信息直接存放,加密或者hash掉密码和敏感的信息。

5.应用的异常信息应该给出尽可能少的提示,最好使用自定义的错误信息对原始错误信息进行包装

6.sql注入的检测方法一般采取辅助软件或网站平台来检测,软件一般采用sql注入检测工具jsky,网站平台就有亿思网站安全平台检测工具

SQL注入漏洞

首先,先看一下我的登录验证的源代码

<?php
header('Content-type:text/html;charset=utf-8');
$db = mysql_connect("localhost","root","root")//mysql_connect()建立数据库连接//localhost:连接MySQL地址//root:连接MySQL用户名//root:连接MySQL密码
    or die("连接数据库失败!");  
mysql_select_db("demo",$db)//mysql_select_db()选择数据库,这里选择的是数据库名为demo的数据库
    or die("您要选择的数据库不存在".mysql_error());
$username=$_POST['username'];//用$username存放从login.html表单提交过来的username
$password=$_POST['password'];//用$password存放从login.html表单提交过来的password
$sql="select * from users where username='$username' and password='$password'";/*用$sql存放sql查询判断命令*/
$result = mysql_query($sql);//mysql_query()规定要发送的SQL查询,查询MySQL,存放于$result。注意:查询字符串不应以分号结束
$colum= mysql_fetch_array($result);//mysql_fetch_array()获取和显示数据
if(is_array($colum)){//is_array()检测变量是否是数组
    if(setcookie('username',$_POST['username'],time()+3600)){//设置cookie
        header("Location:index.php");
    }
    else{
        echo 'Cookie设置失败';
    }
}
else{
    echo "您的用户名或密码输入有误,<a href=login.html>请重新登录!</a>";
}
?>
php复制代码
在这里插入图片描述
在这里插入图片描述

上面的这一部分应该放在另放到一个PHP文件,并把这个PHP文件重点保护。在登录验证时,可以使用require_once() 语句,在脚本执行期间包括并运行这个PHP文件。因为如果攻击者获得了登录验证的源码,就会得到数据库的用户名和密码。

当phpstudy根目录(WWW目录)下的phpMyAdmin文件未删除时,攻击者会通过获得的数据库的用户名和密码,登进phpMyAdmin,查看管理员的数据库。(当然也可以考虑将phpMyAdmin文件删除,这样上面和下面的操作就可以忽略。当然,我没有删除)。

注意:若没有删除phpMyAdmin文件,还需要更改一下phpMyAdmin的登录用户名和密码,这个百度、谷歌上都有教程,可以参考一下。改好后,就可以防止直接从phpMyAdmin进入数据库啦!!

在这里插入图片描述
在这里插入图片描述

观察上面的SQL查询命令,发现了使用动态拼装SQL语句。直接将用户提交过来的数据(用户名和密码)直接拿去执行,并没有实现进行特殊字符过滤,这是十分危险的。

当进行SQL注入攻击时,在用户名输入框中输入:’ or 1=1#,密码随便输入,这时候的合成后的SQL查询语句为:

$sql="select * from users where username='' or 1=1#' and password='$password'";
php复制代码

分析:“#”在mysql中是注释符,这样井号后面的内容将被mysql视为注释内容,这样就不会去执行了,与以下sql语句等价:select * from users where username='' or 1=1

#可以注释掉后面的一行SQL代码 相当于去掉了一个where条件。MySQL 注释, 过滤掉后面的SQL语句,使其不起作用。因为1=1永远是都是成立的,即where子句总是为真。

应对方法:

方法一:

对于这个语句,可以使用下面的技术对用户输入的内容进行过滤:

(1)替换单引号,即把所有单独出现的单引号改成两个单引号,防止攻击者修改SQL命令的含义。再来看这个payload语句,select * from users where username=''' or ''1''=''1' and password=''' or ''1''=''1'显然会得到与select * from users where username='' or '1'='1' and password='' or '1'='1'不同的结果

(2)删除用户输入内容中的所有连字符,防止攻击者构造出类如select * from users where username= 'mas' —— and password =''之类的查询,因为这类查询的后半部分已经被注释掉,不再有效,攻击者只要知道一个合法的用户登录名称,根本不需要知道用户的密码就可以顺利获得访问权限。

(3)对于用来执行查询的数据库帐户,限制其权限。用不同的用户帐户执行查询、插入、更新、删除操作。由于隔离了不同帐户可执行的操作,因而也就防止了原本用于执行select命令的地方却被用于执行insert、update或delete命令。

方法二:

先通过SQL语句查询数据库,取出相关数据

function collect_data(){  
require_once ("mysql_connect.php");  //在脚本执行期间包括并运行mysql_connect.php
$sql = "select * from users";  //查询数据表语句,并用变量$sql存放
$result = mysql_query($sql);//执行MySQL语句,并将结果用变量$result存放
$colum= mysql_fetch_array($result);//获取和显示$result的数据
return $colum;//返回变量$colum的数据
}
php复制代码

然后再将用户提交过来的数据(用户名和密码)和返回的数据进行比对,成功比对才能登录成功。

方法三:

PHP有一个特制的功能以防止这些攻击。所有你需要做的就是使用一个函数mysql_real_escape_string()。 mysql_real_escape_string所做的是把一个输入的字符串,在MySQL查询时将它处理为用户输入的真实字符串,来防止SQL注入。就是将用户输入可能引起Mysql安全隐患的字符串比如单引号(‘),用逃脱引用来表示\ ‘。

将这个函数应用到上面那个可能被注入的例子中:

$username = mysql_real_escape_string($_POST['username']);

$password= mysql_real_escape_string($_POST['password']);

$sql = "select*from users where username= '$username' and password='$password'"; 
php复制代码

这里要十分小心的是,mysql_real_escape_string要先成功地通过mysql_connect连接到mysql server上以后才能正常使用,如果数据库 还没连接直接使用这个函数会报错。上面经过函数转化后,$sql最后打印出来的语句为:

select*from users where username='$username\' OR 1=1 -- and password='$password\' OR 1=1 --' 
php复制代码

也就是说变量$username和$password所存放的数据后面的单引号(‘)被转义为真实的输入字符,不再和$username和$password前的字符进行匹配,$username和$password前的单引号将和字符串–后面的单引号进行匹配。

让我们创建一个通用的函数,你可以用任何名字来命名它,在这里,我要将它命名为”mres”:

function mres($var){
    if (get_magic_quotes_gpc()){/*magic_quotes_gpc作用类似addslashes(),就是对输入的字符创中的字符进行转义处理,所有的 ‘ (单引号), ” (双引号), (反斜线) and 空字符会自动转为含有反斜线的溢出字符*/
        $var = stripslashes(trim($var));//stripslashes删除由 addslashes() 函数添加的反斜杠
    }
    return mysql_real_escape_string(trim($var));
} 
php复制代码

现在,可以把函数简化成下面这个样子:

$username = mres($_POST['username']);
$password = mres($_POST['password']);

$sql = "select*from users where username= '$username' and password='$password'"; 
php复制代码
在这里插入图片描述
在这里插入图片描述

is_array() 函数用于检测变量是否是一个数组,如果检测的变量是数组,则返回 TRUE,否则返回 FALSE。

很明显当SQL语句直接在数据库执行后。没有对结果进行有无特殊字符的判断和过滤。

当然,为了配合漏洞2的第二种应对方法。这里可以进行是否全等的判断

if(($colum['username']===$username) && ($colum['password']===$password)){ 
    if($i==1&&(setcookie('username',$_POST['username'],time()+3600))){
        echo"<script type='text/javascript'>alert('登陆成功');location='index.php';</script>"; 
    }
}
php复制代码

这样就解决了这个问题。

漏洞貌似总结的差不多了耶!开心!!用常规SQL注入方法已经注入不进去了。但本人能力有限,若有其他漏洞,敬请大佬告知啊。本小白感激不尽!!!

小白进阶ing


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 [email protected]

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK