王 欣
(呼倫貝爾學(xué)院網(wǎng)絡(luò)中心 內(nèi)蒙古 海拉爾區(qū) 021008)
隨著互聯(lián)網(wǎng)的深入發(fā)展,各企事業(yè)單位的門戶網(wǎng)站已經(jīng)成為了對(duì)外宣傳和提供信息服務(wù)的窗口。PHP因?yàn)槠涔δ軓?qiáng)大、入門學(xué)習(xí)簡(jiǎn)單、代碼執(zhí)行效率高等優(yōu)點(diǎn),成為了Web應(yīng)用開發(fā)的流行語言。由于廣泛使用,且沒有像針對(duì)ASP網(wǎng)站那么多的入侵工具,所以網(wǎng)站開發(fā)者對(duì)PHP的后臺(tái)并不重視,大部分設(shè)計(jì)者在編寫網(wǎng)站代碼時(shí),對(duì)頁面變量的過濾僅僅參照于 ASP,但這些變量大多數(shù)都不安全。所以利用PHP安全漏洞對(duì)Web網(wǎng)站進(jìn)行攻擊的行為也越來越多,這給Web應(yīng)用的安全帶來了嚴(yán)重的威脅。
呼倫貝爾地區(qū)據(jù)不完全統(tǒng)計(jì)有網(wǎng)站980余個(gè),其中以PHP代碼編寫的網(wǎng)站數(shù)量約占30%。作者長(zhǎng)期以來一直關(guān)注著本地區(qū)的 Web網(wǎng)站安全狀況,近期對(duì)區(qū)域內(nèi)的PHP網(wǎng)站進(jìn)行了一次安全漏洞掃描,根據(jù)掃描結(jié)果來進(jìn)行分析,很多網(wǎng)站存在語義 URL、跨站請(qǐng)求偽造、跨站腳本、SQL注入、文件上傳等安全漏洞。本文將對(duì)呼倫貝爾地區(qū)PHP網(wǎng)站普遍存在的語義URL和SQL注入安全漏洞進(jìn)行分析,并找出解決安全漏洞的方法。
語義 URL攻擊是利用URL特性進(jìn)行的一種攻擊。URL是定位Internet資源的重要手段,在很多的應(yīng)用中,常??梢砸姷嚼?URL 來將參數(shù)傳遞到另一個(gè)程序。如果用戶出于某種目的而改變其中某些參數(shù)的值,這就產(chǎn)生了語義 URL 攻擊。
有關(guān)語義 URL 攻擊的一個(gè)典型例子是 Web應(yīng)用中的密碼找回系統(tǒng)。在實(shí)際應(yīng)用中,很多用戶登錄表單的旁邊常有一個(gè)“忘記密碼”的鏈接,當(dāng)用戶忘記密碼時(shí),可以點(diǎn)此鏈接進(jìn)入到密碼找回系統(tǒng)界面。 密碼找回系統(tǒng)通過詢問用戶特定的問題并根據(jù)用戶的答案來判斷當(dāng)前用戶是否是找回自己的密碼,如果是,則系統(tǒng)將提供一個(gè)表單讓用戶輸入一個(gè)Email地址,以便系統(tǒng)將密碼發(fā)送到此 Email 地址所在的郵箱。接受Email 地址的表單HTML代碼假設(shè)如下所示:
此表單含有一個(gè)隱藏域,用于存儲(chǔ)要找回密碼的用戶名。從上面的表單 HTML代碼可以看出,當(dāng)用戶按下“提交”按鈕時(shí),瀏覽器地址欄將出現(xiàn)以下信息:http://…/resetpass.php?user=Cheng&email=cmhman@yahoo.com.cn
用戶若對(duì) Web 知識(shí)有所了解,其便會(huì)注意到上述 URL 是帶參數(shù)的。如果將上述 URL 中的user 參數(shù)的值改為其他人的用戶名,郵箱地址不變,那么該用戶就可以重置并獲得他人的密碼。
上述密碼找回系統(tǒng)中的語義 URL 攻擊,其發(fā)生的根本原因在于 resetpass.php 沒有對(duì) URL 中提交的 user 參數(shù)值進(jìn)行驗(yàn)證,使得攻擊者有機(jī)可乘,非法重置并竊取他人密碼。為了防范上述密碼找回系統(tǒng)中的語義 URL 攻擊,必須對(duì)用戶身份(即 user 參數(shù)值)進(jìn)行確認(rèn),采用 Session 跟蹤是一個(gè)比較好的方法,代碼如下:
在上述代碼中,$_SESSION['verified']是在用戶正確回答了問題后產(chǎn)生的,用它來和表單隱藏域中的參數(shù)值進(jìn)行比較,便可知道此用戶是否是合法找回密碼的當(dāng)前用戶而不是攻擊者。
Web 應(yīng)用程序未對(duì)用戶輸入進(jìn)行嚴(yán)格驗(yàn)證和過濾, 致使攻擊者可以注入各種特殊字符串、SQL命令等,是導(dǎo)致網(wǎng)站受到 SQL 注入攻擊的根本原因。下面用一個(gè)實(shí)例來具體說明未對(duì)用戶輸入進(jìn)行驗(yàn)證是如何產(chǎn)生 SQL 注入攻擊的。假設(shè)某高校教務(wù)管理系統(tǒng)有一個(gè)學(xué)生成績(jī)查詢頁面,學(xué)號(hào)是查詢學(xué)生成績(jī)的關(guān)鍵字之一。一般情況下,學(xué)生的學(xué)號(hào)都是由數(shù)字組成的字符串,在查詢時(shí),只要輸入合法的學(xué)生學(xué)號(hào),就可以獲得該學(xué)號(hào)所對(duì)應(yīng)學(xué)生的各門課成績(jī)。如果該教務(wù)管理系統(tǒng)在查詢學(xué)生成績(jī)時(shí),采用的是通過輸入?yún)?shù)動(dòng)態(tài)構(gòu)造SQL 語句并提交數(shù)據(jù)庫執(zhí)行的方法,那么,程序的實(shí)現(xiàn)代碼有可能為:
如果查詢者輸入的是合法的學(xué)號(hào)(如 123456 7890),那么提交給MySQL 數(shù)據(jù)庫(此處假定該教務(wù)系統(tǒng)后臺(tái)使用的是 MySQL 數(shù)據(jù)庫,其他數(shù)據(jù)庫的原理相同)執(zhí)行的 SQL 語句最終為:select* from Grade where Sno='1234567890'
該語句是一個(gè)能夠返回學(xué)生成績(jī)的正常 SQL語句,自然不會(huì)出現(xiàn)什么問題。但是,若查詢者不輸入合法的學(xué)號(hào),而是輸入如下參數(shù):1234567890';
delete from Grade where Sno='1234567890'--,則最終提交給MySQL數(shù)據(jù)庫執(zhí)行的 SQL 語句為:
從上述提交給數(shù)據(jù)庫執(zhí)行的SQL語句可以看出,學(xué)號(hào)為 1234567890 的學(xué)生的成績(jī)將被刪除。由此可以看出,如果對(duì)用戶的輸入盲目信任而不加嚴(yán)格驗(yàn)證的話,是可能發(fā)生SQL注入攻擊的。SQL 注入攻擊產(chǎn)生的根本原因是系統(tǒng)開發(fā)人員在開發(fā)Web應(yīng)用系統(tǒng)時(shí)未對(duì)用戶的輸入進(jìn)行嚴(yán)格的驗(yàn)證。因此,針對(duì) SQL用戶輸入的驗(yàn)證,下面是行之有效的一些具體措施:
攻擊者進(jìn)行SQL注入攻擊的常用手段一般是在系統(tǒng)接受用戶輸入的地方注入非法SQL指令并使數(shù)據(jù)庫系統(tǒng)執(zhí)行,采用傳統(tǒng)方法動(dòng)態(tài)構(gòu)造 SQL語句的 Web系統(tǒng)常受此問題困擾。采用參數(shù)化的查詢手段可以在很大程度上對(duì)SQL注入攻擊進(jìn)行有效避免。參數(shù)化查詢方法與傳統(tǒng)的動(dòng)態(tài)構(gòu)造SQL語句有很大的不同。參數(shù)化查詢不會(huì)將用戶的輸入作為SQL命令來執(zhí)行,原因是:采取參數(shù)化查詢方法,數(shù)據(jù)庫系統(tǒng)在執(zhí)行SQL語句時(shí),會(huì)編譯事先設(shè)計(jì)好的SQL指令,然后再套用用戶的輸入?yún)?shù),之后才由數(shù)據(jù)庫執(zhí)行。這樣一來,即便攻擊者輸入的不是合法參數(shù)而是惡意SQL命令,其也不會(huì)得到執(zhí)行,而只能作為參數(shù)處理,因?yàn)榇藭r(shí)SQL語句中的命令部分已經(jīng)編譯完畢,不會(huì)對(duì)用戶輸入的惡意 SQL命令再進(jìn)行編譯了。通過這樣的機(jī)制,絕大部分的SQL注入攻擊都可以得到有效的防范。下面以MySQL數(shù)據(jù)庫為例,看看參數(shù)化查詢的一種具體實(shí)現(xiàn)方法:
使用參數(shù)化查詢方法,可能會(huì)對(duì)程序的維護(hù)及某些功能的實(shí)現(xiàn)帶來不便,但是,這與不使用參數(shù)化查詢,系統(tǒng)可能遭受SQL注入攻擊相比,些許的不便還是值得的,畢竟作為一個(gè)面對(duì)各種類型客戶的Web系統(tǒng)來說,安全是十分重要的。
從SQL注入攻擊產(chǎn)生的原因可知,對(duì)用戶輸入進(jìn)行嚴(yán)格驗(yàn)證,也是行之有效的防范SQL注入攻擊的手段。對(duì)用戶輸入進(jìn)行驗(yàn)證的方法有很多種,要根據(jù)具體情況進(jìn)行選擇??偟恼f來,有如下一些方法:
1.對(duì)用戶輸入數(shù)據(jù)的類型進(jìn)行驗(yàn)證
在Web應(yīng)用程序中,變量的類型大致可以分為三種:數(shù)字型、日期時(shí)間型、字符串型。因此,用戶輸入的數(shù)據(jù)類型一般情況下也不外乎以上三種。對(duì)要求數(shù)字型和日期時(shí)間型的用戶輸入進(jìn)行驗(yàn)證是比較方便的,因?yàn)殚_發(fā)Web應(yīng)用程序的各種腳本語言幾乎都有相應(yīng)的函數(shù)可以用來對(duì)這兩種變量值的類型進(jìn)行判斷。比如,在PHP中,驗(yàn)證用戶的輸入是否為數(shù)字型可以使用函數(shù) is_integer()、is_double(),前者用于驗(yàn)證用戶輸入是否為整數(shù),后者用于驗(yàn)證用戶輸入是否為浮點(diǎn)數(shù);驗(yàn)證用戶的輸入是否為有效的日期時(shí)間,可以使用函數(shù)checkdate()。對(duì)于前面在 SQL 注入產(chǎn)生原因的分析中所舉的例子,因?yàn)閷W(xué)號(hào)一般來說是由數(shù)字組成的字符串,因此,對(duì)輸入學(xué)號(hào)的驗(yàn)證可以采用函數(shù) is_integer()來進(jìn)行,如果判斷下來,用戶輸入的是一個(gè)整數(shù),那么就可以認(rèn)為其輸入的是合法學(xué)號(hào)。
如果 Web 應(yīng)用程序要求用戶輸入的不是數(shù)字類型參數(shù),也不是日期時(shí)間類型參數(shù),而是可能由各種字符組成的一般字符串,那就不能采用判斷數(shù)據(jù)類型的方法來驗(yàn)證用戶的輸入了,而這種情況恰恰又是 Web 應(yīng)用中最常見的。下面研究當(dāng)輸入為一般字符串時(shí)的驗(yàn)證方法。
2.利用正則表達(dá)式對(duì)用戶的輸入進(jìn)行驗(yàn)證
在一般情況下,對(duì)于大多數(shù)Web應(yīng)用程序來說,用戶的輸入不管是在外在形式還是在內(nèi)在屬性上,都是有一定規(guī)律可循的,比如,如果在用戶注冊(cè)及登錄時(shí)要求輸入郵箱信息,由于郵箱具有特定的格式:用戶名@域名,我們就可以構(gòu)造一個(gè)正則表達(dá)式對(duì)用戶的輸入進(jìn)行驗(yàn)證。使用正則表達(dá)式除可以對(duì)用戶的輸入進(jìn)行驗(yàn)證之外, 還可以規(guī)范化用戶的輸入,而規(guī)范化用戶的輸入對(duì)于防范未知的 SQL 注入攻擊形式是及其有效的。
3.對(duì)用戶輸入的長(zhǎng)度進(jìn)行驗(yàn)證
眾多的實(shí)踐表明,對(duì)用戶輸入長(zhǎng)度進(jìn)行驗(yàn)證對(duì)于防范SQL注入攻擊特別是對(duì)于編解碼字符串形式的SQL注入攻擊是十分有效的。利用正則表達(dá)式規(guī)范化用戶的輸入不能防范編解碼形式的SQL注入攻擊,例如:若正則表達(dá)式規(guī)定用戶在輸入字符串中不能包含單引號(hào),但用戶如果將單引號(hào)進(jìn)行編碼后輸入,然后再解碼還原為單引號(hào)的本來形式,正則表達(dá)式就檢測(cè)不出來了。除了單引號(hào)外,對(duì)于其他的字符也可以用編解碼的形式逃過正則表達(dá)式的檢測(cè)。但是,輸入字符通過編碼以后,輸入的長(zhǎng)度會(huì)增加,如果程序?qū)τ脩糨斎氲拈L(zhǎng)度進(jìn)行了規(guī)定,那么編碼后的字符串就不會(huì)被系統(tǒng)所接受,從而有效地防范了編解碼形式的SQL注入攻擊。
對(duì)于一個(gè)已經(jīng)存在并正在使用的PHP網(wǎng)站來說,如果在原先的開發(fā)過程中沒有注意進(jìn)行 SQL注入攻擊方面的防范,自然是十分危險(xiǎn)的。但是,為了防范SQL注入攻擊而全面修改Web應(yīng)用程序代碼,也顯得不太現(xiàn)實(shí),付出的代價(jià)是難以承受的。在這種情況下,為了最大可能防范SQL注入攻擊,可以對(duì)數(shù)據(jù)庫及PHP配置文件進(jìn)行適當(dāng)設(shè)置。在數(shù)據(jù)庫方面的設(shè)置主要有禁止遠(yuǎn)程連接數(shù)據(jù)庫、刪除多余用戶、目錄訪問權(quán)限限制、數(shù)據(jù)庫服務(wù)器權(quán)限控制、禁止多語句查詢等。PHP 配置文件的設(shè)置主要是打開 magic_quotes_gpc 選項(xiàng)和關(guān)閉錯(cuò)誤信息的顯示, magic_quotes_gpc 選項(xiàng)打開以后,系統(tǒng)將對(duì)向數(shù)據(jù)庫提交的 SQL 查詢進(jìn)行轉(zhuǎn)義,從而在相當(dāng)程度上減少了 SQL 注入攻擊的發(fā)生。系統(tǒng)錯(cuò)誤信息顯示關(guān)閉以后,程序運(yùn)行出現(xiàn)錯(cuò)誤時(shí),就不會(huì)在客戶端瀏覽器顯示出具體的錯(cuò)誤信息,這樣可以防止攻擊者進(jìn)行錯(cuò)誤查詢攻擊來獲得數(shù)據(jù)庫敏感信息。
通過對(duì)語義URL和SQL注入漏洞的分析,最終得出了解決這兩項(xiàng)PHP網(wǎng)站主要安全漏洞的方法。只要PHP網(wǎng)站開發(fā)人員和網(wǎng)站管理員提高網(wǎng)絡(luò)安全意識(shí),在開發(fā)和管理網(wǎng)站的過程中采取適當(dāng)?shù)拇胧蟛糠諴HP安全漏洞還是可以防范的。