明輝手游網(wǎng)中心:是一個免費提供流行視頻軟件教程、在線學習分享的學習平臺!

CGI教學:CGI安全問題(6)

[摘要]2.10 處理外部進程  最后,CGI腳本如何與帶有外部過程的用戶輸入打交道是應該警惕的另一區(qū)域。因為執(zhí)行一個位于自己的CGI腳本之外的程序意味著無法控制它做什么,必須盡最大努力在執(zhí)行開始前驗證發(fā)送給它的輸入。  例如,shell腳本經(jīng)常錯誤地將一個命令行程序和表單輸入合在一起執(zhí)行。如果用戶輸入符...
2.10 處理外部進程

 最后,CGI腳本如何與帶有外部過程的用戶輸入打交道是應該警惕的另一區(qū)域。因為執(zhí)行一個位于自己的CGI腳本之外的程序意味著無法控制它做什么,必須盡最大努力在執(zhí)行開始前驗證發(fā)送給它的輸入。

 例如,shell腳本經(jīng)常錯誤地將一個命令行程序和表單輸入合在一起執(zhí)行。如果用戶輸入符合要求,一切都挺正常,但是有可能會加入其它命令并非法執(zhí)行。

 下面即是一個產(chǎn)生了這種錯誤的腳本的例子:

FINGER_OUTPUT='finger$USER_INPUT'
echo $FINGER_OUTPUT

 如果用戶很禮貌地給finger輸入了某人的e-mail地址,一切都會正常工作,但是如果他輸入了一個e-mail地址,后面再跟一個分號和另一條命令,那么該命令也會被執(zhí)行,如果用戶輸入了webmaster@www.server.com;rm-rf/,那麻煩可就大了。

 即使沒有什么隱藏的命令被加入用戶數(shù)據(jù),無意的輸入錯誤也可能帶來麻煩。例如,下面的代碼行會產(chǎn)生一個意料之外的結(jié)果——列出目錄中的所有文件——如果用戶輸入是一個星號的話。

echo "Your input:"$USER_INPUT

 當通過shell發(fā)送用戶數(shù)據(jù)時,就象前面的代碼片段所做的那樣,最好檢查一下shell的meta-character(元字符)——這些可能會導致意外的行為。

 這些字符包括分號(允許一行中有多條命令),星號和問號(完成文件匹配),感嘆號(在csh下指運行的作業(yè)),單引號(執(zhí)行一條包含其中的命令)等等。就像過濾文件名一樣,維護一個允許的字符清單一般要比試圖找出每個不允許的字符容易一些。下面的Perl代碼片段驗證一個e-mail地址:

if ($email_Address ~= /[^a-zA-z0-9_\-\+\@\.]) {
#lllegal character! }
else { system("finger $email_Address"); }

 如果決定在輸入中允許shell元字符,也有辦法讓它們安全一些。盡管可以簡單地給未驗證的用戶輸入加上引號以免shell按特殊字符進行操作,但這實際上不起什么作用。請看下的語句:

echo"Finger information:<HR><PRE>"
finger"$USER_INPUT
echo"</PRE>

 盡管$USER_INPUT上的引號可以使shell不再解釋一個分號,從而不允許黑客簡單地插進來一條命令,但該腳本仍有許多安全方面的漏洞。例如,輸入可能是'rm-rf/',其中單引號可以導致甚至在finger不知道的情況下執(zhí)行黑客的命令。

 一種處理特殊字符的較好的辦法是對它們進行換碼,這樣腳本只是取它們的值而不解釋它們。通過對用戶輸入進行換碼,所有的shell元字符都被忽略并作為增加的數(shù)據(jù)傳給程序。下面的Perl代碼即對非字母數(shù)字字符完成這種處理。

$user_Input=~s/([^w])/\\\1/g;

 現(xiàn)在,如果用戶輸入加在某條命令之后,每個字符——即便是特殊字符——都會由shell傳送給finger。

 不過請記住,驗證用戶輸入——不相信發(fā)送給自己的任何信息——會使自己的代碼更易讀并且執(zhí)行起來更安全。最好不是在已經(jīng)執(zhí)行了命令之后再去對付黑客,而應在門口就對數(shù)據(jù)進行一次性的檢查。

--------------------------------------------

處理內(nèi)部函數(shù)

對于解釋型語言,例如Shell和Perl,如果用戶輸入的數(shù)據(jù)不正確的話,有可能導致程序生成本來沒有的錯誤。如果用戶數(shù)據(jù)被解釋為一部分執(zhí)行代碼,用戶輸入的任何內(nèi)容都必須符合語言的規(guī)則,否則就會出錯。

例如,下面的Perl代碼片段也許會正常工作也許會產(chǎn)生錯誤,這取決于用戶輸入的是什么:

if ($search_Text =~ /$user_Pattern/) {
#Match! }

如果$user_Pattern是一個正確的表達式,一切都會正常,但是如果$user_Pattern不合法;Perl就會失敗,導致CGI程序失敗——這可能是一種不安全的方式。為了避免這種情況,在Perl中至少應有eval()操作符,它計算表達式的值并與執(zhí)行它無關,返回一個碼值表示表達式是有效的還是無效的。下面的代碼即是前面代碼的改進版。
if (eval{$search_Text =~ /$user_Pattern/}) {
if ($search_Text =~ /$user_Pattern/) {
#Match!
}
}

不幸的是,大部分shells(包括最常用的,/bin/sh)都沒有像這樣的簡單的辦法檢查錯誤,這也是避免它們的另一原因。

--------------------------------------------

在執(zhí)行外部程序時,還必須知道傳送給那些程序的用戶輸入是如何影響程序的。編程者可以保護自己CGI腳本不受黑客侵犯,但是如果輕率地將某個黑客輸入的內(nèi)容傳送給了外部程序而不知道那些程序是如何使用這些數(shù)據(jù)的,也會徒勞無益。

例如,許多CGI腳本會執(zhí)行mail程序給某人發(fā)送一個包含用戶輸入信息的e-mail。這可能會非常危險,因為mail有許多內(nèi)部命令,任何一個命令都有可能被用戶輸入激活。例如,如果用mail發(fā)送用戶輸入的文本而該文本有一行以代字號(~)開頭,mail會將該行的下一字符解釋為它能執(zhí)行的許多命令之一。例如,~r/etc/passwd,會導致mail讀取機器的口令文件并發(fā)送給收信人(也許正是黑客自己)。

在這樣的例子中,應該使用sendmail(一個更底層的郵寄程序,它少了許多mail的特性),而不是使用mail在UNIX機器上發(fā)送e-mail。

作為一般規(guī)則,在執(zhí)行外部程序時應該使用盡可能貼近自己要求的程序,不必有過多不必要的功能。外部程序能干的事越少,它被利用來干壞事的機會就越少。

警告

下面是使用mail和sendmail的另一個問題:必須保證發(fā)送給mail系統(tǒng)的是一個合法的e-mail地址。許多mail系統(tǒng)都會把以" "開頭的e-mail地址作為要執(zhí)行的命令,從而為輸入這樣一個地址的黑客打開方便之門,請再一次記住要驗證數(shù)據(jù)。

怎樣才能更好地了解外部程序以便有效地使用它們的另一個例子是grep。grep是一個簡單的命令行實用程序,它在文件中搜索一個常用表達式,表達式可以是一個簡單的串也可以是復雜的字符序列。大部分人會說使用grep不會出什么問題,但是盡管grep可能不會造成什么損失,它卻能被愚弄,下面將說明它是怎么被愚弄的,如下面的代碼所示。它假定在許多文件中完成對用戶輸入項的區(qū)分大小寫的搜索。

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后的內(nèi)容被解釋為其他切換字符時產(chǎn)生錯誤。毫無疑問這不是編程者本來的意圖。在這種情況下它還不太危險,但在其他情況下卻有可能。記住,沒有什么無害的命令,對每條命令部必須從各個角度仔細考慮。

 一般情況下,應該盡可能熟悉自己的CGI腳本執(zhí)行的每個外部程序。對程序知道得越多,就越能保護它們免受數(shù)據(jù)破壞--一方面可以監(jiān)視數(shù)據(jù),另一方面可以禁止某些選項或特性。外部程序經(jīng)常是許多CGI程序問題的一種快速方便的解決辦法——它們都經(jīng)過了測試,可以得到,并且靈活多樣。但它們也可以成為黑客入侵的方便之門。不要害怕使用外部程序——它們經(jīng)常是完成CGI程序中某種功能的唯一辦法——但是要知道它們可能帶來的危害。