top
Loading...
使用Perl編寫CGI時需要注意的幾個問題
一、UNIX與WINDOWS下的差別

  由于PERL在最初是UNIX下的工具,現在雖然PERL已經移植到流行的平臺上:WINDOWS系統,但是運用起來卻是有哪么一點差別,這一點需要引起我們的注意。

  1、文件運算符

  在UNIX下PERL一共有27個文件運算符可以使用,這些運算符使得我們在不打開文件的情況下就可以獲得文件的各種信息。但是在WINDOWS平臺下,我們只能使用其中四個運算符,但萬幸的是這4個運算符功能不錯,基本能滿足我們的需要,這就是下面的那4個運算符:

  -d    測試文件是不是一個目錄
  -e    測試文件是否存在
  -s    測試文件的大小
  -w    測試文件是否是可寫的


前兩個運算符返回的是布爾值(即真或假),第3個運算符返回文件的大小(以字節作為返回方式)。下面是使用方法:

if(-e 'perl.exe'){
  print 'File size is:'-s'perl.exe';
}
else{
  print 'Can't find perl.exen';
}
(-w 'SomeFile')||die "Cannot write to SomeFilen";

  2、郵件的發送

  在UNIX下運用CGI時,我們往往利用sendmail這個著名的程序進行郵件的發送,但是,在WINDOWS下是沒有這個程序的。為了解決這個問題,有人編寫了一個專門的軟件blat,以便在NT下能夠通過SMTP協議發送郵件,我們可以通過在CGI中通過調用blat實現郵件發送功能。有關blat更詳細的消息請參看Windows NT發郵件程序blat使用說明及CGI用法一文。不過,現在我們在WINDOWS+PERL環境下有一個更好的解決辦法,這個辦法不需要第三方軟件的支持,而是利用WINDOWS的Socket模塊實現發送郵件的功能。使用這個方法需要你的NT服務器Socket模塊已經安裝(這幾乎是肯定的),并且你要選取一個可用的SMTP服務器(這個SMTP服務器你可以利用網上的SMTP服務器中選取一個)。下面是實現上述功能的PERL子程序:

sub smtpmail {

my($recipient,$from,$realname,$subject,$mailbody) =@_;

my ($a,$name,$aliases,$proto,$type,$len,$thataddr,$thisaddr,$i);
my $retaddr = $from;

if ($realname) {
  $retaddr = '"'.$realname.'"'." <$from>";
}

&email_err("No recipient!") unless $recipient;

my @TO = split(/,/,$recipient);

$port ||= 25;
my $AF_INET = 2;
my $SOCK_STREAM = 1;
my $sockaddr = 'S n a4 x8';

($name,$aliases,$proto) = getprotobyname('tcp');
($name,$aliases,$port) = getservbyname($port,'tcp') unless $port =~ /^d+$/;
($name,$aliases,$type,$len,$thataddr) = gethostbyname($smtpserver);

my $this = pack($sockaddr, $AF_INET, 0, $thisaddr);
my $that = pack($sockaddr, $AF_INET, $port, $thataddr);

socket(S, $AF_INET, $SOCK_STREAM, $proto) or &email_err("Could not open socket: $!");
bind(S, $this) or &email_err("Could not bind socket: $!");
connect(S,$that) or &email_err("Could not connect to $thataddr: $!");

select(S); $| = 1; select(STDOUT);

if ($doLog == 2) {
  open LOG, ">>$maillogfile" or &email_err("Could not open logfile $maillogfile: $!");
}
$a=<S>; print LOG "$an" if $doLog == 2;
&email_err("SMTP error1: $a") if $a !~ /^2/;
print S "HELO localhostn";

$a=<S>; print LOG "$an" if $doLog == 2;
print S "MAIL FROM:$fromn";
$a=<S>; print LOG "$an" if $doLog == 2;
&email_err("SMTP error2: $a") if $a !~ /^2/;
foreach $i(@TO) {
  print S "RCPT TO:<$i>n";
}
$a=<S>; print LOG "$an" if $doLog == 2;
&email_err("SMTP error3: $a") if $a !~ /^2/;
print S "DATA n";
$a=<S>; print LOG "$an" if $doLog == 2;
print S "From: $retaddrn";
print S "To: $TO[0]";
for ($i = 1; $i < @TO; $i++) {
print S ",$TO[$i]";
}
print S "n";
print S "Subject: $subjectn";
print S "Reply-To: $fromn";
print S "X-Mailer: WinMailer http://$BBS{'smtpserver'}n";
print S $mailbody;
print S "n";
print S "nn";
print S ".n";
$a=<S>;
print LOG "$an" if $doLog == 2;
print S "QUIT";
undef $/;# $_=<DATA>; print;
if ($doLog == 2) {
  close LOG;
}
}

  在調用這個子程序的時候,你需要定義變量$smtpserver,給它一個SMTP服務器的地址,例如:$smtpserver = "mail.abc.com",這個變量可以在子程序中進行定義或是在調用子程序之前。調用這個子程序的方法是:

  &smtpmail("收方郵箱","發方郵箱","發方名字","郵件標題","郵件內容");

  下面是調用的一個例子:

  $smtpserver = "smtp.mail.com"
  $to_mail = "abcde@163.net";
  $from_mail = "matey@hotmail.com";
  $from_name = "小兵";
  $mail_subject = "告訴你一個好消息";
  $body = "..................";
  &smtpamil($to_mail,$from_mail,$from_name,$mail_subject,$body);

  3、文件名和路徑

  在UNIX下,我們可以使用相對路徑對文件進行調用,但是在WINDOWS下一般是需要絕對路徑。在編寫CGI的時候請注意這一點。有時候,我們有可能忘了一點:UNIX是區分大小的,而WINDOWS則是不分大小寫的,在WINDOWS下編好的CGI放到UNIX下運行不了,則有可能是這個問題。

  4、權限問題

  眾所周所,UNIX和NT的文件權限管理有所不同,在UNIX下分三種權限:是否可讀、是否可寫和是否可執行,而在NT下卻有:讀取、寫入、執行、刪除、更改權限、取得所有權等。在UNIX中,我們對具有CGI執行權限的主頁空間一般是這樣設置的:CGI目錄設為711,其它需要給瀏覽者發布信息的目錄設置為666。而在NT下,則你需要根據實際情況作出給予Everyone用戶相應的權限,有時候是可讀可寫入即可,但是有的進候還要增加“可刪除”才行。比如你設置一個論壇,這個論壇的管理中有刪除帖子的功能,這個時候存放帖子的目錄就行給予用戶刪除的權限。


  二、文件名

  文件名是提交給CGI腳本的一種數據,但如果不小心的話,卻能導致許多麻煩。 想要打開一個用戶提供的名字的文件時,都必須嚴格檢查這個文件名以免招至系統重要文件泄露。用戶輸入一個文件名,有可能就試圖打開輸入危險字符串! 例如,用戶輸入的文件名中包含路徑字符,如目錄斜杠和雙點!盡管你期望的是輸入公用的文件名,例如report.txt,但結果卻可能是/report.txt或../../report.txt,系統中所有文件就有可能泄露出去,后果是可想而知的.。
  如果用戶輸入一個已有文件名或對系統的運作有很重要的文件件名!比如輸入的文件名是/etc/passwd,那用戶就可以對該文件任意修改。可能第二天登錄網站時進行更新的時候,你就發現密碼被別人修改了,那時你只有寫信給系統管理員請求幫助了。 所以在編寫CGI腳本時要保證所有字符都是合法的。下面這段代碼能把不合法的字符過濾掉。
  if(($file_name =~ /[^a-zA-Z_.]/) || ($file_name =~ /^./)) {
     print "文件包含有不合法字符,請重新輸入";
     exit;
  }
  最好將上面代碼做為一個子程序,這樣就可以重復地調用它這樣也方便于修改. 對于不允許輸入HTML下面有兩個方案.
1、有種簡單的方法就是不允許小于號(<)和大于(>)因為所有HTML語法必須包 含在這兩個字符間,如果碰到它們就返回一個錯誤是一種防止HTML被提交的簡單的方法. 下面一行Perl代碼快速地清除了這兩個字符:
$user_input=~s/<>//g;
2、復雜一點的方法就是將這兩個字符轉換成它們的HTML換碼(特殊的代碼),用于表示每個字符而不使用該字符本身. 下面的代碼通過全部用& lt;替換了小于符號,用& gt;替換了大于符號,從而完成了轉換:
$user_input=~s/</&lt;/g;
$user_input=~s/>/&gt;/g;



  三、發送MAIL的安全問題

  當你在CGI中使用了一個郵件功能的時候,你是否注意到服務器的安全?黑客可以在利用它獲取任意文件。

  在編寫時要注意以下幾點:

  1、判斷輸入的數據是否為合法的E-MAIL地址,例如:

  if ($names{'mail'}=~tr/;"',*|&!$#()<>[]{}://){  #如果數據帶有命令字符則

    print "Content-type:text/htmlnn";

    print "抱歉你的E-MAIL地址不合法,請您從新輸入";

    exit;

  }

2、在UNIX下使用:“ open(MAIL,"|sendmail -t"); ” 其中-t表示只處理郵件而不對命令進行任何處理!

  4、輸出到瀏覽器

  這是新手要注意的一個問題。
  輸入到瀏覽器的命令是:print "Content-type:text/htmlnn";  ,表示以文本方式在瀏覽器上顯示后面的輸出,切記這行命令后面的nn是不能少的。只有這樣,你才能在瀏覽器上看到CGI的運行結果。另外,
  print "Content-type: image/x-xbitmapnn";
表示以圖像方式輸出到瀏覽器,這個方式在編寫圖形計數器時經常會用到。
北斗有巢氏 有巢氏北斗