萬盛學電腦網

 萬盛學電腦網 >> 健康知識 >> 淺談CGI腳本安全

淺談CGI腳本安全

  很多朋友告訴我一定要去不斷總結自己學的東西,並且不要吝啬拿出來與人分享,這樣才有一個很好的沉澱和交流,還有彼此的促進。於是自己把自己一部分學習筆記整理了一下。還有一些自己平時總結的一些學習經驗,希望對大家有所幫助。

CGI腳本是在網絡服務器上解釋執行的,然後將執行的結果返回給客戶端(就是我們的浏覽器)。因為perl的強大功能、他的靈活性和對所有流行的操作系統的支持,當然還有它的“價格”(純免費)。使其受到了很多CGI編程者的青睐。我們知道任何語言本身是無安全性可言的,安全只是存在於使用他的人的身上。所以如果在編寫腳本時,沒有安全的意識,那麼編寫出的代碼也就沒有安全的"功能"了。

一、注意對變量的處理1、用戶的輸入是不可信任的,當談到安全編程的時候,比爾蓋茨先生對他的員工說了一句非常經典的話:All input is invalid.(所有的用戶輸入都是有害的,具體是不是他第一個說的,有待考察)一切用戶輸入的地方都是我們應當注意的地方。大家知道美國最著名的電子商務網站e***.com吧,他在1999年就被黑客用以下的方法入侵了。我們的新浪網站也被用同一種方法攻了進去,並且還被人截了圖。。。。來看一段代碼吧:-----------Code Start------------#unsafecodz.cgi01 $filepath="f://myhome//bbs//"............13 $filename=$query -> param('page');14 if ($filename eq "")15 { $filename="error.html";16 die("對不起,文件名不能為空!");17 }18 else{19 $filename="$filepath/".$filename;20 open(FILE,$filename);21 while(<FILE>)22 {23 print $_;24 }25 close(FILE)......-----------Code Ends-------------

這段代碼基本上沒有作太多改動,因為我演示是在自己的機子上,所以把路徑改了一下,我們在這裡回放一下曾經的過程。

這段代碼是網站用來浏覽的其他網頁的,如:?page=something.html,就會浏覽something.html這個文件,這裡的$filename用param提取page中用戶輸入的內容,$filename其實是用戶間接輸入的內容,而代碼對$filename並沒有做嚴格的審查只是檢查是否為空,如果惡意用戶直接在浏覽器中指定其他文件,如cgi,asp或者任何文件,則返回的是文件的源代碼,如:?page=unsafecodz.cgi。看到unsafecodz.cgi的源代碼了,通過簡單的浏覽其他的頁面,我們可以基本上可以得到所有文件的源代碼。我們也可以通過"../"來切換到其他的目錄下。這還不是最糟糕的,如果你的系統是Linux或者UNIX,那麼更過分的在這裡呢!?page=/../../../../../../ect/passwd。結果顯而易見,可以看到了主機的用戶名和密碼文檔。這也不是最遭的,如果利用open函數加管道符執行任意命令的後果會怎麼樣?利用open函數執行命令的技術很老了,我就不廢筆墨了。後來有人對代碼進行加固,將第19行改成 $filename="$filepath/".$filename.".html" 他限定後面4位為html,但是這樣的加固似乎起不到什麼作用,因為它忽略了NULL字符。提交如下請求依然可以繞過他的限定:/myhome/bbs/unsafecodz.cgi?page=unsafecodz.cgi 我們看到這行中Cookie: namecookie=/../../../../../ect/passwd

一般隱式輸入都不容易被注意。這裡攻擊者可以通過構造cookie來控制網站,結果就是和上面的一樣。說到這裡其實都是一句話:All input is invalid。作為代碼的編寫者要注意所有用戶可以直接或間接控制的地方。

這裡你可能覺得你似乎明白了,但我敢說其實你並沒有真的明白,cookie的控制是裡面最簡單的一個例子。隱式輸入很多時候,不要說寫代碼的人了,就連很多WEB安全的高手都不見得能夠非常容易的找到,他們更多的是憑自己的經驗和直覺。你可能覺得我言過其實了,其實並沒有,簡單的連傻瓜都能看出來的隱式輸入,當然容易找到。但是許多的危害更大的,並不那麼容易發現,所以這個時候多是憑借自己的經驗和對漏洞"味道"靈敏的嗅覺。

##關鍵詞:沒有##檢查:是否做過有效過濾

解決方法其實很簡單,就是嚴格控制用戶的輸入。所謂嚴格控制並不只是過濾,因為過濾難免有漏網之魚。限定要比過濾來得輕巧和嚴格。把用戶的輸入控制在你規定的范圍內,可以用一個正則表達式來給用戶劃一個范圍,指定用戶可以輸入的字符或者數字,如果用戶輸入與你的規定的不匹配,則不與通過。至於如何做限定,用正則表達式會很簡單。我用email的例子說明:if (email !~/^[\w.-] \@[w.-] $))#如果不和裡面的規定字符匹配則報錯{&error"輸入不正確,難道您就是傳說中的黑客?"}else{#輸入正確,繼續操作。......email的一般格式是[email protected]。我們只希望用戶輸入字符、數字、@、"."、“-”、“_”這些東東。永遠不要幻想用戶會按照你所希望的輸入,除非你給他們劃定范圍。在這裡用以一個簡單的正則表達式。在這個正則表達式中,要求用戶只能輸入英文大小寫字符,數字和“@”,"-","_""."這幾個字符,如果輸入其他的,則報錯。

二、注意幾個危險函數在代碼中的使用和特殊字符過濾。1、有幾個危險函數在程序中用得越少越安全(這麼說好像有點不嚴格,呵呵)。因為很多都是黑客的突破口。這些函數是:system(),open(),exec()。system()和exec都可以執行系統命令,如system("del f:\myhome\$filname"),如果$filename也是通過表單從用戶那裡得到的,如果我們在$filename處輸入1.txt;del f:\myhome。我們就刪除了整個目錄。我們現在可以刪除任意文件。如果你用管道操作符的話也可以。其實system()函數可以執行系統命令,如果對其中變量缺少嚴格限制容易引起安全問題,程序員們或多或少的知道一些,但是由於快速開發或者項目給的時間緊,為了應付差事,往往不會理會。

##關鍵詞:system()、exec()##檢查:函數中是否有可控制的變量,是否可利用

open()函數也是我們應當留意的地方,大家不要誤會,open函數本身是沒有什麼的,而是裡面的用戶輸入的數據導致的問題集中到了open()函數上。open()函數本來是用來打開一些文件,我們看到我們的第一個程序就是因為open函數引起的洩漏源代碼。我們還可以通過“>”,"<"這樣的符號來控制是否向文件寫入或者讀出。如果加上">"則可以向文件中寫入,如果對那些向cgi文件中寫入的字符不加以控制或者過濾不完全,那麼寫入任意的語句就很危險了。其實安全漏洞只不過是代碼編寫者沒有考慮到而惡意用戶卻想到了的地方。一切我們還是讓代碼自己說吧:------------Code Start---------------......my $file = "$lbdir" . "forum$inforum/$intopic.pl";......if (open(FILE, ">$file")){flock(FILE, 2) if ($OS_USED eq "Unix");print FILE "$intopic\t$topictitletemp\t$topicdescription\t$threadstate\t$threadposts\t$threadviews\t$startedby\t$startedpostdate\t$inmembername\t$currenttime\t$posticon\t$inposttemp\t";close(FILE);}------------Code Ends------------------呵呵,這段代碼大家眼熟吧,不錯,這就是LB5K論壇post.cgi中的一段代碼,我覺得這段代碼應該最能說明問題(其實是我手懶,懶的自己寫了),大家注意這裡的open(FILE, ">$file");打開文件准備向裡面寫入。後面的print就是向指定的文件中寫入。論壇的本意是將用戶的貼子標題簡單的保存在一個.pl文件中作為保存,但是編寫者沒有注意到貼子的標題其實是用戶可以任意控制的,如果惡意用戶輸入system函數執行任意指令,如果標題是system @ARGV那麼所保存的.pl記錄文件中的第一行也是system @ARGV,這樣如果去調用那個.pl文件就形成了一個webshell。別急,我還沒說完。前面說過利用open() 管道符同樣可以執行命令。##關鍵字: open()和後面相關聯的變量##檢查:open打開的文件是否可寫,可寫變量是否可以控制。變量是否可控制。

2、特殊字符大部分情況下在用戶輸入的附近,程序員為了保護程序會過濾掉一大批特殊字符。然後用戶輸入的惡意語句無法執行。這個部分其實要說的東西很多,但是總覺得自己有點啰裡啰唆的感覺,為了節省版面簡言之吧。看看下面的字符你是不是都過濾掉了:

反引號(``)反引號我們平時使用的不多,但在perl應用中功能卻很大,,它象system一樣可以執行系統命令,如:System('dir c:\')$a=`dir c:\`;print $a;上下兩條語句執行的結果是一樣的,就算過濾掉system,用反引號可以起到相同的作用。

逗號逗號可能是因為個頭小的緣故,並沒有受到太大的重視,不象他的兄弟分號長得那麼高總受到人家關注,但其實逗號很多時候可以做許多事情,下面兩句話意思是一樣的:$a=`dir C:\`;print $a;$a=`dir C:\`,print $a#

分號分號就不用說了吧,可以截斷程序的流程,比如:system("del ./path/$file");假設$file可控,那麼直接加入jam.txt;del ./path然後整個path目錄就消失了......

反斜槓反斜桿的應用很巧妙,正常的過濾下如果用戶輸入/../會被過濾成/\.\./,但是如果用戶輸入/\.\./呢?則被過濾成了/\.\./,看到了麼?巧妙的輸入,巧妙地逃避了規則限制。

管道符知道這句語句在ls後面加上管道符(|)會起到什麼作用麼? open(FILE, "/bin/ls")知道的話我就不廢話了。

尖括號(<>)跨站腳本是怎麼實現的你不會忘記吧?^_*

美元符($)這個放在perl中的字符串前面是什麼意思?如果用戶用這個字符構造語句輸入,又會產生什麼效果?你不會不知道吧:p

換行符等\t,\r,\x0B,\n,還記得第一個例程我們是怎麼改變程序的流程的麼?

其實這只是一部分,限於篇幅不多說了。

三、驗證的不完全驗證不完全和上下文邏輯錯誤是程序員最容易犯的錯誤。作WEB安全如果能有一個嚴謹的思維那就再好不過了,因為一個嚴謹的思維來去對付那些邏輯錯誤應該是能很容易發現(個人認為)。如果你不幸和我一樣沒有一個成熟而又嚴謹的思維,那麼就多練習多總結來增長自己的經驗值吧。勤能補捉阿!最容易忽略的往往危

copyright © 萬盛學電腦網 all rights reserved