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

CGI教學(xué):CGI安全問題(5)

[摘要]2.7 處理文件名 文件名是提交給CGI腳本的簡單數(shù)據(jù),但如果不小心的話,卻能導(dǎo)致許多麻煩。如果用戶輸入的名字中包含路徑因素,如目錄斜杠和雙點,盡管期望的是輸入一個簡單的文件名--例如file.txt--但結(jié)果卻可能是/file.txt或../../../file.txt。根據(jù)Web服務(wù)器的安裝以...
2.7 處理文件名

文件名是提交給CGI腳本的簡單數(shù)據(jù),但如果不小心的話,卻能導(dǎo)致許多麻煩。如果用戶輸入的名字中包含路徑因素,如目錄斜杠和雙點,盡管期望的是輸入一個簡單的文件名--例如file.txt--但結(jié)果卻可能是/file.txt或../../../file.txt。根據(jù)Web服務(wù)器的安裝以及對提交的文件名做什么操作,系統(tǒng)中的所有文件就有可能都暴露給了一個聰明的黑客。

進一步,如果用戶輸入了一個已有文件的名字或者一個對系統(tǒng)的運行很重要的文件名,怎么辦?對如果輸入的名字是/etc/passwd或C:\WINNT\SYSTEM32\KRNL32.DLL怎么辦?根據(jù)在CGI腳本中對這些文件進行什么操作,它們有可能被發(fā)送給用戶或者被垃圾覆蓋了。在Windows 95和Windows NT下,如果不檢查反斜杠字符(\),可能會允許Web 瀏覽器通過UNC文件名訪問甚至不在該Web機器上的文件。

如果用戶在文件名中輸入了不合法的字符怎么辦?在UNIX下,任何以句點(.)開頭的文件名都是不可見的。在Windows下斜杠(/)和反斜杠(\)都是目錄分隔符。很可能不小心寫了一個Perl程序,當文件名以管(pipe)( )開頭時,盡管自己以為僅僅是打開了一個文件,實際上卻是執(zhí)行了一個外部程序。如果用戶知道怎么辦的話,甚至可以把控制字符(例如Escape鍵或Return鍵)作為文件名的一部分送給腳本。

更壞的情況是,在shell腳本中,分號用于結(jié)束一條命令并開始另一條命令。如果腳本設(shè)計目的是cat用戶輸入的文件,用戶可能輸入file.txt;rm-rf/作為文件名,導(dǎo)致返回fi1e.txt,然后清除整個硬盤而不經(jīng)任何確認。

2.8 輸入合理,輸出卻不合理

為了避免所有這些問題,關(guān)閉由它們打開的所有安全縫隙,檢查用戶輸入的每個文件名。必須確保輸入正是程序預(yù)期的輸入。

這樣做的最好辦法是將輸入的文件名的每個字符與可接收字符的清單進行比較,如果不匹配就返回一個錯誤。這比維持一個所有合法字符的清單并比較它們要安全得多——要想讓什么字符溜掉太容易了。

以下程序清單是用Perl如何完成這種比較的例子。它允許任何字符字母(大寫或小寫調(diào))、任何數(shù)字、下劃線和句點。它還進行檢查以確保文件名不以句點開頭。這樣,該段代碼就不允許可以改變目錄的斜杠,不允許可以將多條命令放在一行的分號,或者破壞Perl的Open()調(diào)用的Pipes了。

程序清單 保證所有字符都是合法的

if (($file_Name =~ /[^a-zA-Z_\.]/) ($file_Name =~ /^\./)) {
#File name contains an illegal characgter or starts with a period
}

警告

盡管上述程序清單中的代碼清除了大部分不合法的文件名,但操作系可能還有一些限制,而該代碼沒有覆蓋到。例如,文件名可以用數(shù)字開頭嗎?或者以下劃線開頭?如果文件中包含多個句點或者句點后多于三個字符怎么辦?整個文件名足夠短得能滿足文件系統(tǒng)的限制嗎?

必須不斷向自己提出這種問題。在寫CGI腳本時最危險的事是認為用戶會遵守指令。其實用戶是不會的。保證用戶不犯錯誤是編程者自己的事。

2.9 處理HTML

另外一種看起來無害的但卻能導(dǎo)致很大麻煩的輸入是在請求用戶輸入文本信息時得到的HTML。以下的程序清單是一個Perl程序片段;它向任何在$user_Name變量中輸入了一個名字的人,例如John Smith,發(fā)出問候信息。

程序清單 發(fā)出定制的問候腳本

print ("<HTML><TITLE>Greetings!<TITLE><BODY>\n");
print ("Hello,$user_Name! It's good to see you!\n");
print ("</BODY><HTL>\n");

想像一下,如果用戶不是僅僅輸入一個名字,而是輸入了<HR><H1><P ALIGN="CENTER">John Smith</P><H1><HR>或想像一下當腳本希望得到用戶名時,黑客輸入了<IMG SRC="http://edu.chinaz.com/secret/cutekid.gif">,結(jié)果是公開了本該保密的信息。允許輸入HTML可能很危險。

比輸入簡單的HTML修改頁面或訪問畫面更危險的是惡意的黑客可能輸入一條服務(wù)器端的include指令。如果web服務(wù)器設(shè)置為服從服務(wù)器端include,用戶就可以輸入

<!--#include file="/secret/project/p1an.txt"-->

而不是他的名字,以便看到秘密計劃的全部文本,或者用戶可以輸入<!--#inc1ude fi1e-"/etc/passwd"-->來獲取機器的口令文件?赡茏顗牡那闆r是黑客可能輸入<!--#exec cmd="rm-rf/"-->而不是他的名字。這樣上述程序清單中的代碼會刪掉硬盤上幾乎所有內(nèi)容。

警告

由于經(jīng)常被惡意地使用,服務(wù)器端的include經(jīng)常被禁止使用以保護站點免受侵害,F(xiàn)在假定這些都沒問題。即使關(guān)閉了服務(wù)器端的include并且不介意用戶能看到自己硬盤上的任何圖片或者改變頁面顯示的外觀,也仍然有問題--不僅是針對編程者的,而且針對其他用戶。

CGI腳本的一個通常用途是留名冊(guestbook):訪問站點的顧客可能簽個名,讓別人知道他們已經(jīng)在那兒了。一般情況下用戶簡單地輸入他的名字,該名字會在訪問者清單中出現(xiàn)。但是,如果將The last signee!<FORM><SELECT>作為用戶名輸入怎么辦?<SELECT>標記將導(dǎo)致Web瀏覽器忽略位于<SELECT>和一個不存在的</SELECT>之間的所有內(nèi)容,包括以后清單中加入的任何名字。即使有10個人簽了名,僅有前3個會顯示出來,因為第三個名字包含一個<FORM>和一個<SELECT>標記。因為第三個簽名者在他的名字中使用了HTML標記,他后面的任何名字都不會顯示出來。

對于用戶輸入HTML而不是普通的文本的情況有兩種解決辦法:

1)快速但比較粗糙的辦法是不允許小于號(<)和大于號(>),因為所有HTML標記必須包含在這兩個字符中,所以清除它們(或者如果碰到它們就返回一個錯誤)是一種防止HTML被提交并返回的簡單的辦法。下面一行Perl代碼簡單地清除了這兩個字符:$user_Input=~s/<>//g;

2)更精細一點的辦法是將這兩個字符轉(zhuǎn)換成它們的HTML換碼--—種特殊的代碼,用于表示每個字符而不使用該字符本身。下面的代碼通過全部用<替換了小于符號,用>替換了大于符號,從而完成了轉(zhuǎn)換:

$user_Input=~s/</&1t;/g;
$user_Input=~s/>/>/g;