PHP注射漏洞

对于脚本来说注入漏洞是最为常见的,也是最大的漏洞之一。在 PHP 的环境中,同样存在注入漏洞,不过我们一般更加习惯称之为注射漏洞。 前面对于注射(注入)漏洞已经多次在 ASP 程序中提及到了,虽然这里是 PHP 环境。但是,他们出现的本质原因都是因为程序没有对我们输入的参数进行过滤就进行查询数据库操作了。在之前介绍手工注入的时候,把注入是从数据库的角度上划分。但是仍然是与所处的语言环境有一定的关系。 下面我们来看一下 PHP程序中最典型的注射漏洞代码,如下所示:
$id=$_GET["id"];
$query="SELECT * FROM my_table where id='".$id."'"; //很经典的 SQL 注入漏洞
$result=mysql_query($query);
参数$id 通过$_GET 方式访问表单域 id 中的内容,利用它可以获得我们输入 id 参数的值。而$id 在没有经过任何过滤情况下,就用字符串连接符(.)连接到了数据库查询代码中,然后就执行了数据库查询。所以对于参数 id 我们可以构造任何数据,从而出现了经典的注射漏洞。
我们已经知道,在 PHP中目前获得客户端输入的数据不仅仅只有$_GET这一种方式。还可以采用$_POST 和$_REQUEST 方式获得数据, 所以对于上面的$id=$_GET["id"];还可以改成$id=$_POST["id"];或$id=$_REQUEST["id"];,不过他们依然存在注射漏洞,只是获取客户端数据的方式不同而已。

这里,我们假设参数 id的正确值为 10。当我们只提交 id=10时,查询语句$query 就等于 SELECT * FROM my_table where id=10,执行结果就是返回 id=10 所包含的信息。当然,我们当然不会那么听话, 只输入id=10。 比如我们输入id=10', 则$query就等于SELECT * FROM my_table where id=10'。因为我们知道在数据库中的参数 id 是不可能有一个 10'的值,所以执行的结果肯定出错,这也是为什么如果存在注入漏洞的时候我们输入单引号页面就会返回错误的原因,这个时候就可以初步判定它存在注入漏洞。 而如果我们输入 id=10 and 1=1的话,则$query 就等于 SELECT * FROM my_table where id=10 and 1=1,前面大家已经知道了 and 的运算规则了,因为 SELECT * FROM my_table where id=10 执行的结果为真,1=1也为真,那么真 and 真运算之后还是为真,所以添加输入 and 1=1 返回的结果依然为真。可是我们输入 id=10 and 1=2 呢?$query 就等于SELECT * FROM my_table where id=10 and 1=2,因为 SELECT * FROM my_table where id=10 为真,1=2 为假,即真 and 假,所以执行的结果为假。 这也是为什么添加输入 and 1=2 返回的结果为错误的原因。所以通过单引号、and 1=1、and 1=2 我们就可以判断一个网站是否存在注射漏洞

PHP下的数字型注射漏洞代码分析

数字型的参数几乎在每一个 PHP 系统都是存在的,而又以在 URL 中出现的最为频繁。如果系统没有对数字型参数进行有效的检测的话,就会发生注射问题。下面我们来看看一个在数字型参数中常见的注射代码,如下所示:
$blogid=$_REQUEST[blogid];
if (!isset($blogid)){
$blogid=1;
}
$query_sql="SELECT * FROM article where id='".$blogid."'";
$result=mysql_query($query_sql);
$pageurl = './?blogid='.$blogid;
参数 blogid在通过$_REQUEST 方式获得其数据之后, 用 isset()函数判断是否存在这个参数值。如果不存在,则把$blogid 的值设置等于 1。而后就放入到了数据库查询语句中,执行该查询,最后在 URL 中输出该参数下的信息。 上面的参数 blogid 仅仅只是验证了其是否存在,如果存在的话,就放入数据库中进行查询。而并没有检测 blogid 中我们到底输入了什么数据,即没有检测输入数据的合法性,这就意味着我们只要输入数据就会执行数据库查询操作。这就引发了注射漏洞的出现,利用该参数我们可以查询数据库中的任何信息。 假设使用这个系统的网站为 http://www.xxx.com/,其后台数据库是 MySQL, 出现注射漏洞的页面为 index.php,且$blogid 的值等于 5。那么存在注射漏洞的 URL 地址为:http://www.xxx.com/index.php?$blogid=5。当我们输入http://www.xxx.com/index.php?$blogid=5 and 1=1 和http://www.xxx.com/index.php?$blogid=5 and 1=2 就可以判断出这个系统存在注入,在前面已经为大家介绍了。
要判断后台数据库是否为 MySQL,输入http://www.xxx.com/index.php?$blogid=5/*hack,那么到了数据库中就变成了 SELECT * FROM article where id=5/*hack;。因为 MySQL 支持/*注释,所以它会把/*hack 当作解释来处理。那么输入$blogid=5/*hack 与输入$blogid=5 是一样的,结果自然也就返回是正确的, 不过支持/*解释的也只有MySQL数据库一种, 所以可以利用这个特性来判断是否是MySQL数据库。对于更多的手工注入方法这里就不在重复了,不清楚的话可以参考第二章。只是大家要明白,我们在手工注入过程中输入的那些额外数据,如 and 1=1、and 1=2 等等。他们都是被放入到了数据库执行的语句中去执行了,从而获得更多规定以外的数据。归根到底就是没有过滤参数的数据。 在 PHP 中要防止数字型注射漏洞是比较简单的,我们可以用到 PHP 自带的一些函数。例如使用转义字符串函数 addslashes(), 提交的变量中所有的 ' (单引号), " (双引号), \ (反斜线) and 空字符会自动转为含有反斜线的转义字符。例如,我们输入$blogid=5',使用函
数 addslashes()之后,它就会把$blogid=5'转换成$blogid=5\',这样我们的注射就根本没办法进行下去了,彻底的断绝了注射攻击。所以上面的代码,我们可以改成:

$blogid= addslashes($_REQUEST[blogid]);
//使用addslashes()函数对参数所有的单引号、双引号、反斜杠、and、空字符都进行转义
if (!isset($blogid)){
$blogid=1;
}
$query_sql="SELECT * FROM article where id='".$blogid."'";
$result=mysql_query($query_sql);
$pageurl = './?blogid='.$blogid;
那么使用了 addslashes()函数对输入的参数 blogid 转换之后,就可以彻底防止注入漏洞的存在。 还有一种办法也可以彻底断绝数字型注射漏洞,使用函数 intval()对输入的数据进行转换。该函数的作用是把数据进行强制转换成数字型,比如输入$blogid=5',使用函数intval($blogid),它就会把 5'强制性的转换成数字 5。使用该函数后,所有的数据都会被转换成数字。所以对于数字型参数,只要使用了这个函数那么不管参数是什么都被转换成数字。那么同样可以彻底防止数字型的注射攻击,所以上面的代码,我们还可以改成:
$blogid=intval($_REQUEST[blogid]); //使用intval()函数对输入的参数值全部强制性转换成数字
if (!isset($blogid)){
$blogid=1;
}
$query_sql="SELECT * FROM article where id='".$blogid."'";
$result=mysql_query($query_sql);
$pageurl = './?blogid='.$blogid;
上面的代码都是由我自己写的,虽然其存在注射漏洞,但是缺乏一定的实战性。这里为大家初步分析一下系统中存在的注射漏洞,这是一个叫南部弯的 PHP 论坛中存在的注射漏洞代码,如下所示: