网络安全自学篇-渗透测试(随笔四)

浅析漏洞防范

本文简单介绍在代码中如何避免漏洞的产生,以较为常见的漏洞为例。

  1. SQL注入漏洞:在编写操作数据库的代码时,将外部变量直接拼接到SQL语句中且没有经过任何过滤机制就放入数据库中执行。

魔术引号:
magic_quotes_gpc:负责对GET、POST、COOKIE的值进行过滤,然而在php6、7中已经取消了该函数,因此我们可以自己定义一个函数来为数据加上\,即将所有外部变量用addslashes函数过滤:

<?php
function quotes_gpc($str) {
    
    
    if(!get_magic_quotes_gpc()) {
    
    
        $str = addslashes($str);
        echo "转义的结果:".$str;
    }
    return 0;
}
$s=$_GET['s'];
quotes_gpc($s);

但这样也仅能防御部分注入,宽字节注入依旧会产生:
在这里插入图片描述
mysql_real_escape_string:负责对字符串进行过滤,但从php7就被移除了,这里还是举个例子:

<?php
$conn = mysql_connect("localhost", "root", "root");
$id = mysql_real_escape_string($_GET['id'],$conn);
$sql = "select * from admin where id='".$id."'";
echo $sql
?>

当上文请求参数?id=1’是,会输出:select * from admin where id='1\''
intval等字符转换:在上面的方法中面对int型的注入并无效果,容易被通过报错和盲注的形式进行注入,这时候可以使用intval将外部变量转换为int型:

<?php
$id = intval($_GET['kid']);
echo $id;

很明显,当请求参数?kid=1 union select 1,2,执行结果:
在这里插入图片描述
PDO prepare预编译:PHP pdo类似于.NET的SqlParameter或者java里的prepareStatement,都是通过预编译的方法来处理查询,如下代码中第5行,PDO::ATTR_EMULATE_PREPARES设置为false来禁止php进行本地模拟prepare,该行为会导致参数转义,gbk编码下依旧会产生SQL宽字节注入:

<?php
$name = "jadore";
$pass = "123456";
$pdo = new PDO("mysql:host=localhost; dbname=test", "root", "root");
$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$pdo->exec("set names 'utf8'");
$sql = "select * from user where username = ? and password =?";
$stmt = $pdo->prepare($sql);
if($stmt->execute(array($name,$pass))) {
    
    
    echo $name."登录成功!";
}
  1. XSS漏洞:分为反射型,存储型,DOM型,Web应用读取危害代码并输出在页面上。

HTML实体转义:利用htmlspecialchars(),htmlentities()函数,转义<、>、&等字符

<?php
$arrs = array("<>","&");
foreach ($arrs as $arr) {
    
    
    echo htmlentities($arr)."\n";
}

在这里插入图片描述
标签事件属性白名单:通过对标签事件的白名单,即使用正则表达式来匹配,如果匹配到的事件不在白名单内,直接拦截,而不是将其替换为空。
3. ###### CSRF漏洞:劫持其他用户进行某些恶意请求。
token验证:令牌是防范CSRF较好的一种方式,简单地理解就是在页面或者COOKIE中添加一段不可猜解的字符串,而服务器在接收用户请求时会验证该字符串是否为上次访问留下的即可判断是否为非法请求,如果用户没有访问上一个页面,token是很难获取到的:

<?php
session_start();
function setToken() {
    
    
    $_SESSION['token'] = md5(time()+rand(1,1000));
    echo $_SESSION['token']."\n";
}
function checkToken() {
    
    
    if(isset($_POST['token']) && $_POST['token'] === $_SESSION['token']) {
    
    
        return true;
    } else {
    
    
        return false;
    }
}

if(isset($_SESSION['token']) && checkToken()) {
    
    
    echo "token验证成功\n";
} else {
    
    
    echo "token验证失败\n";
}
setToken();
?>
<form method="post">
    <input type="text" name="token" value="<?=$_SESSION['token']?>">
    <input type="submit">
</form>

首次访问页面时token验证失败,因为我们从未打开这页面,token也还没获取
在这里插入图片描述
而当我们提交相同的token时:
在这里插入图片描述
验证码验证:这对于用户的体验会产生影响,不可能每个页面都要求用户去填写验证码,因此应用场景类似于登录页面。

  1. 文件上传漏洞:用户上传文件,文件后缀名并未被服务端严格检查,导致传入可被服务器解析的恶意脚本。

白名单过滤文件拓展名:比如使用in_array或者===来校拓展名
重命名文件,采用时间戳拼接随机数等方式:

<?php
function getExt($filename) {
    
    
    return substr($filename,strripos($filename,'.')+1);
}
$allowExt = array('jpg','png','gif');
$filenameExt = strtolower(getExt($_FILES['file']['name']));

if(!in_array($filenameExt,$allowExt)) {
    
    
    die("不允许的文件名");
} else {
    
    
    $filename = md5(time()+rand(1,1000)).".".$filenameExt;
    move_uploaded_file($_FILES['file']['tmp_name'],"upload/".$filename);
}
  1. 代码执行漏洞:应用程序本身过滤不严,导致用户通过请求将代码注入应用中并执行。

采用白名单过滤参数,以该代码举例:

<?php
preg_replace('/(\w+)\|(.*)/ie', '$\\1="\\2";', $_GET['a']);

在这里插入图片描述
假设我们知道\2的范围是纯数字,那么将正则改为:

<?php
preg_replace('/(\w+)\|(\d+)/ie', '$\\1="\\2";', $_GET['a']);

在这里插入图片描述

  1. 命令执行漏洞:相关的命令执行函数内参数过滤不严导致用户可以执行系统或者应用命令。

escapeshellarg ( string $arg ) : string:在字符串周围添加单引号,并附加引号,然后从字符串中转义单引号。这样使得$arg在确保最大安全性的同时,将参数直接作为Shell参数传递,简单来说就是过滤参数,将参数限制在一对双引号里,此时再引入其他字符串会转为空格:

echo(escapeshellarg($_GET['cmd']));

在这里插入图片描述

escapeshellcmd ( string $command ) : string
escapeshellcmd()转义字符串command 中在shell命令中可能具有特殊含义的所有字符。此函数可确保将命令正确传递给Shell exec()和 system()命令执行程序 ,或带有反斜杠标记。过滤的字符为:&;|`*?~<>^()[]{}$\ \x0A \xFF %,而’、"仅仅在不成对的时候被转义,可以看到escapeshellcmd 分别在Windows和Linux会添加

^和\
echo(escapeshellcmd($_GET['cmd']));

在这里插入图片描述
参数白名单:参数白名单是一种比较通用的修复方法,利用正则表达式即可,这里边不再记录。
7. ##### 变量覆盖漏洞:函数使用不当。
有个不错的例子我记录下来:

$a=1;
foreach(array('_COOKIE','_GET','_POST') as $_request) {
    
    
    foreach ($$_request as $_key => $value) {
    
    
        echo $_key.'<br />';
        $$_key = addslashes($value);
    }
}
echo $a;

上图代码,假使我们提交参数?a=2那么
在这里插入图片描述
使用初始变量:不进行变量注册,直接使用初始的$_GET之类的变量进行操作,若需要注册,则可以在代码中定义变量,然后在请求中赋值。
验证变量是否存在,可以使用if语句,也可以使用extract函数的第二个参数EXTR_SKIP或者parse_str函数。

猜你喜欢

转载自blog.csdn.net/weixin_44047795/article/details/108558644