PHP程序漏洞产生的原因分析与防范方法说明

滥用include

1.漏洞原因:

Include是编写PHP网站中最常用的函数,并且支持相对路径。有很多PHP脚本直接把某输入变量作为Include的参数,造成任意引用脚本、绝对路径泄露等漏洞。看以下代码:

...
$includepage=$_GET["includepage"];
include($includepage);
...

很明显,我们只需要提交不同的Includepage变量就可以获得想要的页面。如果提交一个不存在的页面,就可以使PHP脚本发生错误而泄露实际绝对路径(这个问题的解决办法在下面的文章有说明)。

2.漏洞解决:

这个漏洞的解决很简单,就是先判断页面是否存在再进行Include。或者更严格地,使用数组对可Include的文件作出规定。看以下代码:


代码如下:

$pagelist=array("test1.php","test2.php","test3.php"); //这里规定可进行include的文件
if(isset($_GET["includepage"])) //判断是否有$includepage
{
$includepage=$_GET["includepage"];
foreach($pagelist as $prepage)
{
if($includepage==$prepage) //检查文件是否在允许列表中
{
include($prepage);
$checkfind=true;
break;
}
}
if($checkfind==true){ unset($checkfind); }
else{ die("无效引用页!"); }
}

这样就可以很好地解决问题了。

小提示:有此问题的函数还有:require(),require_once(),include_once(),readfile()等,在编写的时候也要注意。

未对输入变量进行过滤

1.漏洞原因:

这 个漏洞早在ASP中出现过,当时造成的注入漏洞不计其数。但由于PHP在当时的影响力较小,所以没有太多的人能够注意这点。对于PHP来说,这个漏洞 的影响性比ASP更大,因为有比较多的PHP脚本使用到文本型数据库。当然也存在SQL语句的注入问题。举个比较经典的例子,首先是数据库的:


代码如下:

$id=$_GET["id"];

$query="SELECT * FROM my_table where id='".$id."'"; //很经典的SQL注入漏洞
$result=mysql_query($query);

这里很明显我们可以用注入来获得数据库的其它内容了。这里就不再详细叙述,和ASP注入一样的,大家可以看看以前的黑防。然后我们看文本数据库的问题:


代码如下:

$text1=$_POST["text1"];
$text2=$_POST["text2"];
$text3=$_POST["text3"];

$fd=fopen("test.php","a");
fwrite($fd,"\r\n$text1&line;$text2&line;$text3");
fclose($fd);

文本的漏洞可以说是更加严重。倘若我们的提交的变量中插入一段很小的PHP代码,就可以另这个文本数据库test.php变成PHP后门。甚至插入上传代码,让我们可以上传一个完善的PHP后门。接着提升权限,服务器就是你的了。

2.漏洞解决:

这个漏洞的解决方法其实很简单,就是严格对全部提交的变量进行过滤。对一些敏感的字符进行替换。我们可以借助PHP提供的htmlspecialchars()函数来替换HTML的内容。这里给出一段例子:


代码如下:

//构造过滤函数
function flt_tags($text)
{
$badwords=array("操","fuck"); //词汇过滤列表
$text=rtrim($text);
foreach($badwords as $badword) //这里进行词汇的过滤
{
if(stristr($text,$badword)==true){ die("错误:你提交的内容含有敏感字眼,请不要提交敏感内容。"); }
}
$text=htmlspecialchars($text); //HTML替换
//这两行把回车替换为

$text=str_replace("\r"," ",$text);
$text=str_replace("\n","",$text);
$text=str_replace("&line;","│",$text); //文本数据库分隔符"&line;"替换为全角的"│"
$text=preg_replace("/\s{ 2 }/"," ",$text); //空格替换 中国网管联盟
$text=preg_replace("/\t/"," ",$text); //还是空格替换
if(get_magic_quotes_gpc()){ $text=stripslashes($text); } //如果magic_quotes开启,则进行\'的替换
return $text;
}

$text1=$_POST["text1"];
$text2=$_POST["text2"];
$text3=$_POST["text3"];

//过滤全部输入
$text1=flt_tags($text1);
$text2=flt_tags($text2);
$text3=flt_tags($text3);

$fd=fopen("test.php","a");
fwrite($fd,"\r\n$text1&line;$text2&line;$text3");
fclose($fd);

经过一番替换和过滤后,你就可以安全地把数据写入文本或数据库了。

管理员判断不完全

1.漏洞原因:

我们用PHP写脚本,通常要涉及管理员的权限问题。而一些脚本仅仅对管理员权限作出"是"判断,而往往忽略了"否"判断。在PHP配置文件中 register_globals打开的情况下(4.2.0以后版本默认关闭,但有不少人为了方便而打开它,这是极度危险的行为),就会出现提交变量冒充 管理员的情况。我们看一下的例子代码:


代码如下:

$cookiesign="admincookiesign"; //判断是否Admin的cookie变量
$adminsign=$_COOKIE["sign"]; //获取用户的cookie变量

if($adminsign==$cookiesign)
{
$admin=true;
}

if($admin){ echo "现在是管理员状态。"; }

看上去好像很安全的样子,呵呵。现在我们假设PHP配置文件中register_globals为打开状态。我们提交这样一个地址“test.php? admin=true”,结果看到了吗?我们虽然没有正确的Cookie,但由于register_globals为打开状态,使得我们提交的admin 变量自动注册为true。而且脚本缺少“否”判断,就使得我们顺利地通过admin=true取得管理员的权限了。这个问题存在于大部分网站和论坛当中。

2.漏洞解决:

解决这个问题,我们只需要在脚本中加入对管理员的“否”判断即可。我们仍然假设PHP配置文件中register_globals为打开状态。看一下的代码:


代码如下:

$cookiesign="admincookiesign"; //判断是否Admin的cookie变量
$adminsign=$_COOKIE["sign"]; //获取用户的cookie变量

if($adminsign==$cookiesign)
{
$admin=true;
}
else
{
$admin=false;
}
if($admin){ echo "现在是管理员状态。"; }

这 样,就算攻击者在没有正确Cookie的情况下提交了admin=true的变量,脚本在以后的判断中也会把$admin设置为False。这样就解 决了部分的问题。但由于$admin是变量,倘若在以后的其他脚本引用中出现了漏洞使得$admin被重新赋值就会引发新的危机。因此,我们应该使用常量 来存放管理员权限的判定。使用Define()语句定义一个admin常量来记录管理员权限,在此以后若配重新赋值就会出错,达到保护的目的。看以下代 码:


代码如下:

$cookiesign="admincookiesign"; //判断是否Admin的cookie变量
$adminsign=$_COOKIE["sign"]; //获取用户的cookie变量

if($adminsign==$cookiesign)
{
define(admin,true);
}
else
{
define(admin,false);
}
if(admin){ echo "现在是管理员状态。"; }

值得注意的是,我们使用了Define语句,所以在调用Admin常量时前面不要习惯性的加变量符号$,而应该使用Admin和!admin。

文本数据库暴露

1.漏洞原因:

前面已经说过,由于文本数据库具有很大的灵活性,不需要任何外部支持。加上PHP对文件的处理能力十分强,因此文本数据库在PHP脚本中的应用甚广。甚至有几个很好的论坛程序就是使用文本数据库的。但有得必有失,文本数据库的安全性也是比其他数据库要低的。

2.漏洞解决:

文 本数据库作为一个普通的文件,它可以被下载,就像MDB一样。所以我们要用保护MDB的办法来保护文本数据库。把文本数据库的后缀名改为.PHP。并 在数据库的第一行加入。这样文本数据库就会作为一个PHP文件,并且在第一行退出执行。也就是返回一个空页面,从而达到保护文本数据库的目的。

错误路径泄露

1.漏洞原因:

PHP遇到错误时,就会给出出错脚本的位置、行数和原因,例如:

Notice: Use of undefined constant test - assumed 'test' in D:\interpub\bigfly\test.php on line 3

有很多人说,这并没有什么大不了。但泄露了实际路径的后果是不堪设想的,对于某些入侵者,这个信息可是非常重要,而事实上现在有很多的服务器都存在这个问题。

有些网管干脆把PHP配置文件中的display_errors设置为Off来解决,但本人认为这个方法过于消极。有些时候,我们的确需要PHP返回错误的信息以便调试。而且在出错时也可能需要给用户一个交待,甚至导航到另一页面。

2.漏洞解决:

PHP从4.1.0开始提供了自定义错误处理句柄的功能函数set_error_handler(),但很少数脚本编写者知道。在众多的PHP论坛中,我只看见很少一部分对此情况进行了处理。set_error_handler的使用方法如下:

string set_error_handler ( callback error_handler [, int error_types])

现在我们就用自定义的错误处理把实际路径过滤掉。


代码如下:

//admin为管理员的身份判定,true为管理员。
//自定义的错误处理函数一定要有这4个输入变量$errno,$errstr,$errfile,$errline,否则无效。
function my_error_handler($errno,$errstr,$errfile,$errline)
{
//如果不是管理员就过滤实际路径
if(!admin)
{
$errfile=str_replace(getcwd(),"",$errfile);
$errstr=str_replace(getcwd(),"",$errstr);
}

switch($errno)
{
case E_ERROR:
echo "ERROR: [ID $errno] $errstr (Line: $errline of $errfile)
\n";
echo "程序已经停止运行,请联系管理员。";
//遇到Error级错误时退出脚本
exit;
break;
case E_WARNING:
echo "WARNING: [ID $errno] $errstr (Line: $errline of $errfile)
\n";
break;
default:
//不显示Notice级的错误
break;
}
}

//把错误处理设置为my_error_handler函数
set_error_handler("my_error_handler");

这样,就可以很好地解决安全和调试方便的矛盾了。而且你还可以花点心思,使错误提示更加美观以配合网站的风格。不过注意两点是:

(1)E_ERROR、 E_PARSE、E_CORE_ERROR、E_CORE_WARNING、E_COMPILE_ERROR、 E_COMPILE_WARNING是不会被这个句柄处理的,也就是会用最原始的方式显示出来。不过出现这些错误都是编译或PHP内核出错,在通常情况下 不会发生。

(2)使用set_error_handler()后,error_reporting ()将会失效。也就是所有的错误(除上述的错误)都会交给自定义的函数处理。
其它有关于set_error_handler()的信息,大家可以参考PHP的官方手册。

POST漏洞

1.漏洞原因:

前面已经说过,依靠register_globals来注册变量是个不好的习惯。在一些留言本和论坛程序中,更要严格检查获得页面的方式和提交的时间间隔。以防止灌水式发帖和外部提交。我们看一下以下某留言本程序的代码:


代码如下:

...
$text1=flt_tags($text1);
$text2=flt_tags($text2);
$text3=flt_tags($text3);

$fd=fopen("data.php","a");
fwrite($fd,"\r\n$text1&line;$text2&line;$text3");
fclose($fd);

很 明显的,如果我们提交网址”post.php?text1=testhaha&text2=testhaha&text3= testhaha”。数据就会被正常写入文件中。此程序并没有检测变量的来源和浏览器获得页面的方式。如果我们向这个页面重复多次提交,就会起到洪水的作 用。现在也有一些软件利用这个漏洞来在论坛或留言本上发广告,这是可耻的行为(我朋友的留言本就在1星期内被灌了10多页,无奈)。

2.漏洞解决:

在 进行数据处理和保存前,首先判断浏览器的获得页面方式。使用$_SERVER["REQUEST_METHOD"]变量来获得浏览器的获得页面方式。 检查其是否为”POST”。在脚本中使用session来记录用户是否通过正常途径(即填写提交内容的页面)来提交数据。或使用$_SERVER ["HTTP_REFERER"]来检测,但不推荐这样做。因为部分浏览器没有设置REFERER,有部分防火墙也会屏蔽REFERER。另外,我们也要 对提交内容检查,看数据库中是否有重复内容。以留言本为例,使用Session进行判定:
填写浏览内容的页面中,我们在最前端加上:

$_SESSION["allowgbookpost"]=time(); //登记填写时的时间

在接受留言数据并保存的页面中我们在进行数据处理前我们也用Session进行以下处理:


代码如下:

if(strtoupper($_SERVER["REQUEST_METHOD"])!=”POST”){ die("错误:请勿在外部提交。"); } //检查页面获得方法是否为POST
if(!isset($_SESSION["allowgbookpost"]) or (time()-$_SESSION["allowgbookpost"] < 10)){ die("错误:请勿在外部提交。"); } //检查留言填写时的时间
if(isset($_SESSION["gbookposttime"]) and (time()-$_SESSION["gbookposttime"] < 120)){ die("错误:两次提交留言的间隔不得少于 2 分钟。"); } //检查留言间隔

unset($_SESSION["allowgbookpost"]); //注销allowgbookpost变量以防止一次进入填写页面多次进行提交
$_SESSION["gbookposttime"]=time(); //登记发送留言的时间,防止灌水或恶意攻击
...

数据处理及保存

(0)

相关推荐

  • 需要注意的几个PHP漏洞小结

    需要注意的几个PHP漏洞 几个重要的php.ini选项 Register Globals php>=4.2.0,php.ini的register_globals选项的默认值预设为Off,当register_globals的设定为On时,程序可以接收来自服务器的各种环境变量,包括表单提交的变量,而且由于PHP不必事先初始化变量的值,从而导致很大的安全隐患. 例1: 复制代码 代码如下: //check_admin()用于检查当前用户权限,如果是admin设置$is_admin变量为true,然后下

  • PHP代码网站如何防范SQL注入漏洞攻击建议分享

    黑客通过SQL注入攻击可以拿到网站数据库的访问权限,之后他们就可以拿到网站数据库中所有的数据,恶意的黑客可以通过SQL注入功能篡改数据库中的数据甚至会把数据库中的数据毁坏掉.做为网络开发者的你对这种黑客行为恨之入骨,当然也有必要了解一下SQL注入这种功能方式的原理并学会如何通过代码来保护自己的网站数据库.今天就通过PHP和MySQL数据库为例,分享一下我所了解的SQL注入攻击和一些简单的防范措施和一些如何避免SQL注入攻击的建议. 什么是SQL注入(SQL Injection)? 简单来说,SQ

  • PHP漏洞全解(详细介绍)

    针对PHP的网站主要存在下面几种攻击方式: 1.命令注入(Command Injection) 2.eval注入(Eval Injection) 3.客户端脚本攻击(Script Insertion) 4.跨网站脚本攻击(Cross Site Scripting, XSS) 5.SQL注入攻击(SQL injection) 6.跨网站请求伪造攻击(Cross Site Request Forgeries, CSRF) 7.Session 会话劫持(Session Hijacking) 8.Ses

  • php5系列的apache远程执行漏洞攻击脚本

    php5.x系列/apache远程执行漏洞及攻击脚本以下为相关代码,请文明使用... 复制代码 代码如下: /* Apache Magica by Kingcope *//* gcc apache-magika.c -o apache-magika -lssl *//* This is a code execution bug in the combination of Apache and PHP.On debian and Ubuntu the vulnerability is presen

  • ThinkPHP框架任意代码执行漏洞的利用及其修复方法

    ThinkPHP是国内著名的开源的PHP框架,是为了简化企业级应用开发和敏捷WEB应用开发而诞生的.最早诞生于2006年初,原名FCS,2007年元旦正式更名为ThinkPHP,并且遵循Apache2开源协议发布.早期的思想架构来源于Struts,后来经过不断改进和完善,同时也借鉴了国外很多优秀的框架和模式,使用面向对象的开发结 构和MVC模式,融合了Struts的Action和Dao思想和JSP的TagLib(标签库).RoR的ORM映射和ActiveRecord模式, 封装了CURD和一些常

  • php中Y2K38的漏洞解决方法实例分析

    本文实例分析了php中Y2K38漏洞的解决方法.分享给大家供大家参考.具体分析如下: Y2K38,又称 Unix Millennium Bug, 此漏洞将会影响到所有 32 位系统下用 UNIX 时间戳整数来记录时间的 PHP,及其它编程语言. 一个整型的变量所能保存的最大时间为 2038 年 1 月 19 日 03:14:07.超过这个时间后,整型数值将会溢出. 从 1970 年 01 月 01 日开始,到世界标准时 2038 年 01 月 19 日星期二凌晨 03:14:07 超过 2^31

  • php中sql注入漏洞示例 sql注入漏洞修复

    在开发网站的时候,出于安全考虑,需要过滤从页面传递过来的字符.通常,用户可以通过以下接口调用数据库的内容:URL地址栏.登陆界面.留言板.搜索框等.这往往给骇客留下了可乘之机.轻则数据遭到泄露,重则服务器被拿下. 一.SQL注入的步骤 a)  寻找注入点(如:登录界面.留言板等) b)  用户自己构造SQL语句(如:' or 1=1#,后面会讲解) c)  将sql语句发送给数据库管理系统(DBMS) d)  DBMS接收请求,并将该请求解释成机器代码指令,执行必要的存取操作 e)  DBMS接

  • 对于ThinkPHP框架早期版本的一个SQL注入漏洞详细分析

    ThinkPHP官网上曾有一段公告指出,在ThinkPHP 3.1.3及之前的版本存在一个SQL注入漏洞,漏洞存在于ThinkPHP/Lib/Core/Model.class.php 文件 根据官方文档对"防止SQL注入"的方法解释(参考http://doc.thinkphp.cn/manual/sql_injection.html) 使用查询条件预处理可以防止SQL注入,没错,当使用如下代码时可以起到效果: $Model->where("id=%d and usern

  • PHP编程中的常见漏洞和代码实例

    不是固若金汤,随着PHP的广泛运用,一些黑客们也在无时不想找PHP的麻烦,通过PHP程序漏洞进行攻击就是其中一种.在节,我们将从全局变量,远程文件,文件上载,库文件,Session文件,数据类型和容易出错的函数这几个方面分析了PHP的安全性. 如何通过全局变量进行攻击? PHP中的变量不需要事先声明,它们会在第一次使用时自动创建,它们的类型根据上下文环境自动确定.从程序员的角度来看,这无疑是一种极其方便的处理方法.一旦一个变量被创建了,就可以在程序中的任何地方使用.这个特点导致的结果就是程序员很

  • PHP 小心urldecode引发的SQL注入漏洞

    Ihipop 学校的 Discuz X1.5 论坛被黑,在那里吵了一个下午.Google 一下"Discuz! X1-1.5 notify_credit.php Blind SQL injection exploit",你就知道. Discuz 是国内很流行的论坛系统,被黑的网站应该会很多吧.不过我对入侵别人的网站不感兴趣,同时也鄙视那些代码都不会写只会使用别人放出的工具攻击的所谓的"黑客". 粗略看了一下代码,这个 SQL 注入漏洞是 urldecode 函数造成

  • php漏洞之跨网站请求伪造与防止伪造方法

    伪造跨站请求介绍伪造跨站请求比较难以防范,而且危害巨大,攻击者可以通过这种方式恶作剧,发spam信息,删除数据等等.这种攻击常见的表现形式有: 伪造链接,引诱用户点击,或是让用户在不知情的情况下访问 伪造表单,引诱用户提交.表单可以是隐藏的,用图片或链接的形式伪装. 比较常见而且也很廉价的防范手段是在所有可能涉及用户写操作的表单中加入一个随机且变换频繁的字符串,然后在处理表单的时候对这个字符串进行检查.这个随机字符串如果和当前用户身份相关联的话,那么攻击者伪造请求会比较麻烦.如果攻击者以隐藏的方

  • php is_numberic函数造成的SQL注入漏洞

    一.is_numberic函数简介国内一部分CMS程序里面有用到过is_numberic函数,我们先看看这个函数的结构bool is_numeric (mixed $var)如果 var 是数字和数字字符串则返回 TRUE,否则返回 FALSE.二.函数是否安全接下来我们来看个例子,说明这个函数是否安全. 复制代码 代码如下: $s = is_numeric($_GET['s'])?$_GET['s']:0;$sql="insert into test(type)values($s);"

随机推荐