if ($search_Text =~ /$user_Pattern/) {
#Match! }
如果$user_Pattern是一个正确的表达式,一切都会正常,但是如果$user_Pattern不合法;Perl就会失败,导致CGI程序失败——这可能是一种不安全的方式。为了避免这种情况,在Perl中至少应有eval()操作符,它计算表达式的值并与执行它无关,返回一个码值表示表达式是有效的还是无效的。下面的代码即是前面代码的改进版。
if (eval{$search_Text =~ /$user_Pattern/}) {
if ($search_Text =~ /$user_Pattern/) {
#Match!
}
}
不幸的是,大部分shells(包括最常用的,/bin/sh)都没有像这样的简单的办法检查错误,这也是避免它们的另一原因。
--------------------------------------------
在执行外部程序时,还必须知道传送给那些程序的用户输入是如何影响程序的。编程者可以保护自己CGI脚本不受黑客侵犯,但是如果轻率地将某个黑客输入的内容传送给了外部程序而不知道那些程序是如何使用这些数据的,也会徒劳无益。
例如,许多CGI脚本会执行mail程序给某人发送一个包含用户输入信息的e-mail。这可能会非常危险,因为mail有许多内部命令,任何一个命令都有可能被用户输入激活。例如,如果用mail发送用户输入的文本而该文本有一行以代字号(~)开头,mail会将该行的下一字符解释为它能执行的许多命令之一。例如,~r/etc/passwd,会导致mail读取机器的口令文件并发送给收信人(也许正是黑客自己)。
在这样的例子中,应该使用sendmail(一个更底层的邮寄程序,它少了许多mail的特性),而不是使用mail在UNIX机器上发送e-mail。
作为一般规则,在执行外部程序时应该使用尽可能贴近自己要求的程序,不必有过多不必要的功能。外部程序能干的事越少,它被利用来干坏事的机会就越少。
警告
下面是使用mail和sendmail的另一个问题:必须保证发送给mail系统的是一个合法的e-mail地址。许多mail系统都会把以"|"开头的e-mail地址作为要执行的命令,从而为输入这样一个地址的黑客打开方便之门,请再一次记住要验证数据。
怎样才能更好地了解外部程序以便有效地使用它们的另一个例子是grep。grep是一个简单的命令行实用程序,它在文件中搜索一个常用表达式,表达式可以是一个简单的串也可以是复杂的字符序列。大部分人会说使用grep不会出什么问题,但是尽管grep可能不会造成什么损失,它却能被愚弄,下面将说明它是怎么被愚弄的,如下面的代码所示。它假定在许多文件中完成对用户输入项的区分大小写的搜索。
print("The following lines contain your term:<HR><PRE>");
$search_Term=~s/([^w])/\\\1/g;
system("grep $search_Term/public/files/*.txt");
print(<"PRE>");
这一切看起来挺好,除非考虑到用户可能会输入-i。它不会被搜索,而是作为与grep的切换,就像任何以连字符开头的输入一样。这会导致grep或者因等待将搜索的串输入标准输入而挂起,或者如果-i后的内容被解释为其他切换字符时产生错误。毫无疑问这不是编程者本来的意图。在这种情况下它还不太危险,但在其他情况下却有可能。记住,没有什么无害的命令,对每条命令部必须从各个角度仔细考虑。
一般情况下,应该尽可能熟悉自己的CGI脚本执行的每个外部程序。对程序知道得越多,就越能保护它们免受数据破坏--一方面可以监视数据,另一方面可以禁止某些选项或特性。外部程序经常是许多CGI程序问题的一种快速方便的解决办法——它们都经过了测试,可以得到,并且灵活多样。但它们也可以成为黑客入侵的方便之门。不要害怕使用外部程序——它们经常是完成CGI程序中某种功能的唯一办法——但是要知道它们可能带来的危害。
三、内部伤害
到目前为止,仅仅考虑了通过Web例览站点的人——从几千里之外——可能带来的潜在的安全危险。但实际上还存在另一种离得更近的危险因素。
在CGI安全问题上常犯的一种错误是忘记了本地用户。尽管通过Web浏览站点的人不影响本地安全,如文件保护和所有者,但Web服务器的本地用户却能这样,必须做出更多努力防止这些入侵。大部分多用户系统上,如UNIX,Web服务器是作为一个程序运行的,而机器仍被许多人使用做着许多事情。仅仅因为为某人与自己一起工作或访问自己的学校并不意味着他能抵制住诱惑,而不去捣鼓Web安装从而引起问题。
3.1 CGI脚本用户
大部分Web服务器是作为运行CGI脚本的特殊用户而安装的。这是在CGI程序运行时拥有该CGI程序的用户,并且他所拥有的权限能限制该脚本能做什么事情。
在UNIX下,服务器自己也是作为root(系统的超级用户或管理员)运行的,并允许它使用端口80作为浏览器与之通信的地方(只有root能使用这些被称为"保留的"端口0到1023;所有用户都可以使用其余的端口)。当服务器执行CGI程序时,大部分Web服务器都能设置为以另外一个用户而不是Web服务器本身来运行该程序——尽管不是所有服务器都能这么做。
将CGI脚本作为root运行是很危险的!服务器应被设为利用一个普通用户,如常用的nobody来运行CGI脚本。用户权限越小,运行的CGI脚本能造成的危害就越小。如Apache WebServer在启动后会将用户权限自动切换为nobody。
3.2 Setuid 危险
编程者还应知道自己的UNIX CGI脚本中是否设置Setuid位。如果对某个可执行文件允许该选项,将能使该程序与拥有该文件的用户有同样权限,而不是执行它的用户。如果自己的CGI脚本上设置setuid位,无论服务器作为什么用户来运行它,它的权限都等同于该文件的拥有者。这当然有很大的隐患--可能会对以其权限运行脚本的用户失去控制。幸运的是Setuid位很容易被禁止。对所有CGI脚本执行chmod a-s即能关闭所有的setuid,程序即能以允许的权限运行。
当然,在某些情况下也许希望设置setuid位--例如如果脚本需要以特殊用户身份来运行以访问一个数据库。在这种情况下,必须加倍小心确保该程序的其他文件保护能将可以访问它的用户限制在允许范围内。
3.3 "Community" Web服务器
即使Web服务器以一个常用的用户来执行脚本,仍有一个潜在的问题,那就是一个人并不总是能控制服务器。如果许多人共同控制服务器,每个人都可以将CGI脚本安装作为nobody用户来运行。这就使这些人的任何一个都可以利用CGI程序访问他们原先不能访问的地方,而这些地方是nobody允许进入的。
也许潜在的安全问题的解决办法是将CGI的控制限制为一个人。在某些情况下尽管这似乎是合理的,但对较大站点却经常不太可能。例如,一个大学有几百个学生,每个学生都想试着去编写并安装CGI脚本。
3.4 使用CGI Wrap
当有多个用户可以访问CGI时,对于确定脚本以什么用户运行的问题的一个较好的解决办法是CGI wrap程序。CGI Wrap,可以在using CGI Web站点中找到,是一个简单的包装,它以拥有该文件的用户而不是服务器指定的用户来运行CGI脚本。这种简单的预防措施使脚本拥有者对它可能的危害负责。
因为CGI wrap使得CGI脚本的作者负责他们自己的脚本权限,所以它不仅是一个保护其他人拥有的重要文件的有力的工具,而且是促使人们编写安全的脚本的有力的工具。只有他们自己的文件会处于危险之中,这样的现实对脚本作者会是极大的促进。
3.5 CGI脚本权限
还应该清楚了解CGI脚本被哪个用户拥有以及脚本自身的文件权限。包含脚本的目录的权限也非常重要。
例如,如果Web服务器上的cgi-bin目录是所有人可写的,那任何本地用户将能删除CGI脚本并用另一个来代替。如果脚本本身是所有人可写的话,那么任何人将能修改脚本完成任何事情。
请看下面这段无害的UNIX CGI脚本:
#!/bin/sh
#Send the header
echo"Content-type:tex/html"
echo""
#Send some HTML
echo "<HTML><HEADER><TITLE>Fortune</1TLE><HEADER>
echo "<Body>Your fortune:<HR><PRE>
forune
echo"</BODY><HIML>"
现在,如果脚本上设置的权限允许某个恶意的用户将程序改变如下:
#!/bin/sh
#Send the header
echo "content-type:text/html"
echo""
#Do some damage!
rm-rf/
echo"<HTML><TITLE>Got you! <TITLE><BODY>"
echO"<H1>Ha ha!<H1></BODY></HTML>"
那么下一个在Web上访问该脚本的用户即使他没做什么坏事也会导致大量问题。在Web上检查用户输入的完整性很重要,但更重要的是保证脚本本身未被修改且不能被修改。
3.6 本地文件安全
脚本在本地硬盘上创建的文件的完整性也同样重要。在得到Web用户输入的一个合理的文件名之后,使用该文件名干什么也很重要。根据Web服务器运行的操作系统,权限和拥有者信息可以与文件中的数据一起存在文件上。
例如,UNIX系统能记录文件访问权限,包括创建该文件的用户的权限、同组用户的权限、以及系统其他人的权限。windows NT使用的是一个更复杂访问控制清单系统,但完成的功能大致相同。根据这些标志如设置以及授予或禁止什么权限,Web服务器机器的用户也可能引起麻烦。
例如,在创建一个文件时就应知道给它设置的权限。大部分Web服务器软件将umask或权限码设为0000,意味着可以创建一个任何人可读写的文件。尽管文件上的权限设置对在Web上浏览的人可能没什么不同,但本地访问的用户却能利用不严格的权限设置造成危害。基于这种现实,应该尽可能严格地限制文件权限。
保证每个打开文件的调用都有一个最小限制集合的最简单的办法是设置脚本的umask。umask()是一个UNIX调用,它能对每个后续的文件创建限制权限。umask()的参数是一个数字,用于对后续的文件创建的权限码进行屏蔽。如果umask为0022,则不管在打开文件时给组用户和其他用户赋予了什么显式的权限,都将导致创建的文件仅能被用户自己写。即使已经设置了umask,创建文件时也应该显式指定权限。如果只有CGI脚本能访问文件,那么只有运行CGI程序的用户才能访问该文件——权限为0600。如果另一个程序需要访问该文件,可以使该程序的拥有者成为与CGI脚本同一组的用户,这样只需设置组用户权限——权限为0660。如果必须让所有人都能访问该文件,应使该文件只能读,不能写——权限为0644。
3.7 使用显式路径
最后,本地用户还可以最后一种方式攻击Web服务器——欺骗服务器运行他写的一个外部程序,而不是运行在CGI脚本中指定的程序。下面是一个简单的程序,从UNIX的fortune命令可以看出该浏览者还比较聪明。
#!/bin/sh
# Send the header
echo"conten_type:text/html"
echo""
#Send the fortune
echo"<HTML><HEADER><TITLE>Fortune</TITLE></HEADER><BODY>"
echo "<You crack open the cookie and the fortune reads:<HR><PRE>"
fortune
echo "</PRE><BODY></HTML>"
该脚本看起来可一点没有害处。它不接收用户输入,所以用户不能籍此搞什么把戏。因为它仅由Web服务器运行,所以脚本本身的权限设置可以非常严格,可以防止任何有企图的本地用户修改它。如果对该脚本所在的目录也设置了正确的权限的话,看起来就没什么地方可以出问题了,是不是?
当然还有问题。记住得要有点偏激。
上述程序清单调用了外部程序,在本例中是echo和fortune。因为这些脚本没有用显式路径指明它们在硬盘上的位置,该shell即使用PATH环境变量来找到它们,从变量中的每一项查找要执行的程序。这可能很危险。例如,如果fortune程序安装在/usr/games中,但PATH中在它之前列出了/TMP,那么任何碰巧命名为"fortune"并位于临时目录的程序都会被执行,而不是真正的fortune。
该程序可以做它的创建者想做的任何事情,可以删除文件,也可以登记有关请求信息并将数据传给真正的fortune——使用户和编程者谁也不聪明。在CGI脚本中运行外部程序时一定要指定显式的路径。PATH环境变量有很大作用,但它与其他变量一样也能被非法使用。
RSS订阅






收 藏
推 荐