Perl語言安全

引言

對一種編程語言而言,在設計這種語言的時候,一般是不會産生安全隱患的,事實上,這種隱患是由程序員引入的。幾乎每一種編程語言都有一定這樣的漏洞,這種漏洞將會在某種程度上導致不安全軟件的産生,但是一個如軟件整體的安全性仍然大部分依賴于這個軟件制造者的知識面、理解能力和他的安全意識。Perl也有它安全上令人擔憂的部分,然而大多數程序員完全沒有意識到這些方面。

在這篇文章裏,我們將會看一下Perl中一些最普遍被誤用和忽視的屬性。我們將會看到它們的誤用將會怎樣對運行它們的系統的安全以及它們的用戶造成威脅。我們將會演示怎樣把這些弱點挖掘出來以及如何去修改、避免它們。

用戶輸入上的弱點

Perl腳本中産生安全問題的一個很大的來源是沒有經過正確確認(或根本就沒有確認)的用戶的輸入。每次當你的程序要從一個不信任用戶那裏獲取輸入信息的時候,即使采用的是非直接的方式,你都應該小心。舉個例子來說吧,如果你在Perl中寫CGI腳本,你要預期到惡意的用戶將會發送給你假的輸入。 不正確的用戶輸入,如果沒有經過確認就被認可並使用了,將會導致許多方面出錯。最常見和明顯的錯誤是,沒有經過確認就去執行有用戶自定義參數的其他程序。

syetem()和exec()函數

Perl以能被用作一種“粘合”語言而著稱――它能夠通過如下方式完成一個出色的工作:在調用其他程序來爲它工作的時候,通過采集一個程序的輸出,將它重新格式成一種特定的方式後傳遞到其他程序的輸入的方式仔細的協調它們的運行。這樣各個程序就能很好的運行了。

正如Perl發布標語告許我們的,我們有不止一種方法可以做同樣的事。

一種執行一個外部程序和一個系統命令的方法是通過調用exec()函數。當Perl遇到一個exec()語句的時候,它審視exec()被調用處的參數,然後啓動一個新的進程來執行這條特定的命令。Perl從不會返回控制給調用exec()的原來的那個進程。

另一個相似的函數是system()。system()的運行方式非常象exec()。它們之間的唯一的大的區別是Perl會首先從父進程中分叉出一個子進程,子進程作爲提供給system()的一個參數。父進程等到子進程結束運行後再接著運行程序的其余部分。我們將會在下面更詳細的討論system()調用,但這些討論大部分也適用于exec()。

傳遞給system()的參數是一個列表――列表裏的第一個元素是要被執行的這個程序的程序名,其他元素是傳給這個程序的參數。然而,如果只有一個參數的的話,system()的執行方式會發生差異。在那種情形下,Perl將會掃描這個參數看它是不是包含任何shell轉換字符。如果有的話,它就要把這些字符通過shell來解釋。所以産生一個shell命令行來工作。不然,Perl會降字符串拆成單詞然後調用效率更高的c庫函數execvp(),這個函數不能理解特殊的shell字符。

現在假設我們有一張CGI表單,它要詢問用戶名,然後顯示包含這個用戶統計信息的一個文件。我名可以如下使用system()來調用’cat’實現那種要求:

system ("cat /usr/stats/$username");

用戶名來自這樣的一個表單:

$username = param ("username");

. 舉個例子,當用戶在表單裏添上username = jdimov,然後提交後。Perl在字符串``cat /usr/stats/jdimov''中沒有找到任何轉換字符創,所以它就調用execvp()函數運行”cat”後返回到我們的腳本中。這個腳本也許看起來沒有害處可言,但是它容易被一個惡意的攻擊者所利用。

問題是這樣的,通過在表單的”username”域內使用特殊的字符,一個攻擊者可以通過 shell來執行任何命令。舉個例子,我們可以這樣說,如果攻擊者傳遞這樣的字符串"jdimov; cat /etc/passwd",Perl會把分號當作一個轉換字符,然後把它傳遞到shell中:

cat /usr/stats/jdimov; cat /etc/passwd

攻擊者既可以獲得亞元文件,又可以獲得密碼文件。如果攻擊者想要搞破壞的話,他只要發送"; rm rf /*"就可以了。

我們在前面提到system()有一個參數表,並且將第一個元素看作命令來執行,而將其余的元素作爲參數來傳遞。所以我們可以稍微改變一下我們的腳本,使只有我們想讓執行的程序能夠被執行:

system ("cat", "/usr/stats/$username");

既然我們分開來指定程序的參數,那麽shell就永遠也不會被調用了。所以發送";rm -rf /*"也就不會起作用了,因爲攻擊字符串將只會被解釋成一個文件名而已。

這種方法比單個參數的版本要好多了,因爲它避免了使用shell命令,但是仍然有潛在的缺陷。特別的,我們要考慮到$username的值會不會被利用産生程序中能被執行的弱點。舉例來說,一個攻擊者仍然可以利用我們重寫的代碼版本,通過把$username設置成字符串"../../etc/passwd"來獲得系統的密碼文件。

使用那樣的程序的時候很多地方會出錯,舉例來說,一些應用程序將特殊的字符序列解釋成執行一條shell命令的請求。一個普遍的問題是有些版本的Unix郵件工具當它們在一定的上下文背景下看到有”~!…”等字符序列的時候將會執行一個shell命令。所以在一個消息體中的空白行中包含"~!rm -rf *"的用戶輸入將會在某種情形下産生問題。

只要是談及安全的,上面論及system()函數的任何內容也適用于exec().

Open()函數

在Perl中open()函數被用來打開文件。在最爲常見的形式中,它是這樣使用的:

open (FILEHANDLE, "filename");

這樣使用的時候,’filename”是以只讀方式打開的。如果”filename”是含有””標志的前綴,那麽它是爲輸出而打開的,並且在文件已經存在的時候覆蓋原文件;如果含有””前綴,那麽是爲追加打開的;前綴”

open (STATFILE, "/usr/stats/$username");

然後我們從文件中讀取代碼並顯示它。Perl文檔告許我們:如果文件名是以”|”開始的,文件名將會被解釋成一個輸出管道命令;反之,如果文件名以”|”結束的話,文件名將會被解釋成將讓我們進行輸出的管道。

于是,只要加上一個”|”前綴,用戶就可以在/usr/stats目錄下運行任何命令了。向後回溯目錄的操作能夠讓用戶在這個系統裏執行任何程序。

一種解決這個問題打方法是:對于你想要打開並向其中輸入的文件總是要求通過加”

有時我們確實要調用一個外部的程序,比如,我們想要改表我們的腳本文件以讓他能夠讀取舊的純文本文件/usr/stats/username,但是在顯示給用戶之前要先通過一個HTML過濾器。我們有一個馬上就可以使用的便利的方法來實現這個意圖。一種方法可以這樣做:

open (HTML, "/usr/bin/txt2html /usr/stats/$username|");

print while ;

不幸的是,這依然要通過shell層。然而我們可以采用open()調用的另一個形式來避免牽涉到shell:

open (HTML, "-|")

or exec ("/usr/bin/txt2html", "/usr/stats/$username");

print while ;

當我們打開一個管道命令,或者是爲了讀(“-|”),或者是爲了寫(”|-“)的時候,Perl在當前進程中産生分支,並且返回子進程的PID給父進程,返回0給子進程。”or”語句用來決定我們是在父進程還是在子進程。如果我們在父進程(返回值爲非零),我們繼續執行print()語句。否則我們在子進程中,就執行txt2html程序,使用多于一個參數的exec()的安全版本來避免傳遞任何命令到shell層。所發生的是,子進程答應txt2html産生的STDOUT輸出,然後就默默的消亡了(記住:exec()從不返回),同時父進程從STDIN中讀取結果。象這樣的技術可以被用來通過管道將輸出輸到一個外部程序的技術:

open (PROGRAM, "|-")

or exec ("/usr/bin/progname", "$userinput");

print PROGRAM, "This is piped to /usr/bin/progname";

在我們需要管道的時候,open()的以上這些形式應該總是比直接的管道open()命令優先采用,因爲它們不通過shell層。現在讓我們設想我們要將靜態文本轉化成格式化很好的HTML頁面,並且,基于方便考慮,要存放在顯示這些頁面的Perl腳本相同的目錄下。那麽我們的open語句看起來可能是如下形式:

open (STATFILE, "

當用戶通過表單中傳遞username=jdimo的時候,腳本顯示jdimov.html。這裏仍然有被攻擊的可能。不同于c++和c ,perl不用空字節來結束字符串,這樣的話字符串jdimov/”jdimov/lo/bah在絕大數c庫調用中解釋爲”jdimo”,但是在Perl中卻是”jdimov\0blah”。當perl傳遞一個含空字符的字符串給用c寫的程序的時候,這個問題就突出了。UNIX內核以及絕大多數UNIX 和shell 都 是純c 語言的。Perl自身也主要是且c編寫,當用戶如下調用我們的腳本:

statscrit.plusername=jdimov/%00

會發生什麽呢?我們的程序傳遞字符串”jdimov/%。html”到對應的系統調用裏以打開它,但是因爲那些系統調用是用c編寫,接受的是空字節的字符串方式。結果怎樣呢?如果有文件”jdimov”的話就會顯示這個文件,可能並沒有這個文件,即使有也不是很有用。但是如果用"statscript./pusername=statscript。p/%"來調用腳本,會發生什麽呢?如果腳本和我們的html文件在同一個目錄下的話,這樣我們可以用這

Perl語言安全
翻譯:eastdark(eastdark) 來源:http://www.seceye.com by lavender http://www.seceye.com 引言 對一種編程語言而言,在設計這種語言的時候,一般是不會産生安全隱患的,事實上,這種隱患是由程序員引入的。幾乎...查看完整版>>Perl語言安全
 
Perl的安全性監測
<編者按:不要讓 CGI 擔上“世界上最流行的安全漏洞”的名聲。 Nathan將教給你如何使用Perl的內部安全機制。> 本月的專欄將介紹Perl的稱爲“tainting”的內部安全機制,它可以讓Perl捕捉到任何可能導致安全性問...查看完整版>>Perl的安全性監測
 
Perl的安全性監測
<編者按:不要讓 擔上“世界上最流行的安全漏洞”的名聲。 Nathan將教給你如何使用Perl的內部安全機制。> 本月的專欄將介紹Perl的稱爲“tainting”的內部安全機制,它可以讓Perl捕捉到任何可能導致安全性問題...查看完整版>>Perl的安全性監測
 
Linux腳本語言PERL的模板應用分析
  在編制PERL程序時,經常會用到很多相似的HTML代碼。這樣不但書寫起來會很麻煩,而且一旦要改動頁面,而程序很大,那就更麻煩了。  解決這樣的問題一個好的方法就是使用模板。通常的模板使用方法有兩種:  一...查看完整版>>Linux腳本語言PERL的模板應用分析
 
Perl語言存取MSQL和MySQL數據庫內容
  1) 知識准備:  爲了使用Perl語言去存取mSQL和MySQL數據庫的內容,必須安裝一些API模塊,以下列出一些必須安裝的模塊名稱說明和其下載網址:  i) 常規安裝必須的模塊:  DBI-1.13  Perl語言的數據庫通用...查看完整版>>Perl語言存取MSQL和MySQL數據庫內容
 
在Linux系統下安裝Perl腳本語言
  linux 和 perl 都屬于自由軟件,將二者結合真是妙不可言。  遵循以下步驟一般就可安裝好 perl ,perl 就能在 linux 下歡唱。  1 取得最新版本的 perl,當前版本爲 5.6.0,即 stable.tar.gz。  2 解文件包:...查看完整版>>在Linux系統下安裝Perl腳本語言
 
perl語言編程實例-多進程篇
  作者:horsley  perl 語言編程實例-多進程篇  perl 語言是一種非常強大的腳本語言,其應用遍及系統維護,CGI,數據庫編程。  以下是我遇到的一個具體問題,應用perl獲得圓滿解決。  問題提出:  某數...查看完整版>>perl語言編程實例-多進程篇
 
用Perl語言進行Socket編程
  網絡編程是一門神秘且複雜的藝術,當然也十分有趣。Perl語言提供了豐富的TCP/IP網絡函數,所有這些函數都  直接來源于C語言的socket庫函數.  由于Perl語言和C語言的socket庫函數在型式和使用方法上都是一樣的...查看完整版>>用Perl語言進行Socket編程
 
Perl語言全面編譯
  簡  述  本文將詳細講述Perl的編譯方法,獻給所有熱愛、喜歡Perl的程序員們。  Perl自從面世以來1.0版本到現今的5.6版本,一直都有編譯程序,主要因爲國內的中文資料很少,大多數人不願意去看或者不懂得英...查看完整版>>Perl語言全面編譯
 
 
回到王朝網路移動版首頁