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

編寫安全的ASP代碼

[摘要]ASP中數(shù)據(jù)庫(kù)的安全是一個(gè)很嚴(yán)肅的問(wèn)題。很多代碼的編寫者意識(shí)到了這類問(wèn)題,并且小心翼翼地對(duì)他們認(rèn)為有問(wèn)題的地方做了補(bǔ)救,但常見的情況是要么沒(méi)有窮盡所有的可疑地點(diǎn),要么這種補(bǔ)救邏輯上有誤。對(duì)于一個(gè)耐心且嗅覺靈敏的攻擊者來(lái)說(shuō),這種意義上的補(bǔ)救措施和沒(méi)有任何補(bǔ)救措施沒(méi)有本質(zhì)上區(qū)別。 下面羅列的是一些...
ASP中數(shù)據(jù)庫(kù)的安全是一個(gè)很嚴(yán)肅的問(wèn)題。很多代碼的編寫者意識(shí)到了這類問(wèn)題,并且小心翼翼地對(duì)他們認(rèn)為有問(wèn)題的地方做了補(bǔ)救,但常見的情況是要么沒(méi)有窮盡所有的可疑地點(diǎn),要么這種補(bǔ)救邏輯上有誤。對(duì)于一個(gè)耐心且嗅覺靈敏的攻擊者來(lái)說(shuō),這種意義上的補(bǔ)救措施和沒(méi)有任何補(bǔ)救措施沒(méi)有本質(zhì)上區(qū)別。
  下面羅列的是一些可能出現(xiàn)的問(wèn)題:有些是常見易犯的錯(cuò)誤,有些根本就是邏輯上有問(wèn)題?纯茨闶遣皇且策@樣寫過(guò)?對(duì)于攻擊者而言,倒著看這些東西,應(yīng)該對(duì)尋找漏洞有點(diǎn)幫助,更為完整一點(diǎn)的檢測(cè)方法,請(qǐng)等我的關(guān)于黑/白盒分析和自動(dòng)化測(cè)試文章。

一、令人疑惑的過(guò)濾方式

  典型例子是不管不顧地對(duì)所有的輸入變量都去掉單引號(hào),或者是把單引號(hào)替換成合法的兩個(gè)單引號(hào),例如:

  id = replace(request.querystring("id"), "'", "")
  str = replace(request("someinput"), "'", "''")

  現(xiàn)在很明了的是,第一個(gè)做法很有可能是錯(cuò)誤的。因?yàn)橐餝QL Injection的不總是單引號(hào),再擴(kuò)大一點(diǎn),引起問(wèn)題的不是任何單獨(dú)的符號(hào),這樣子的過(guò)濾,有些冤枉單引號(hào)了。正確的利用注入,重要的一點(diǎn)是閉合前面的一句SQL查詢語(yǔ)句——往往是得先正確地閉合前面一個(gè)條件,因?yàn)槲覀兛赡軙?huì)在同一句里面引入新的條件,補(bǔ)救措施只要破壞注入條件應(yīng)該就可以了,但是考慮到其復(fù)雜性(下面會(huì)說(shuō)),最好還是較為完整的限制一下輸入的字符種類。
  第二個(gè)看起來(lái)是沒(méi)有什么問(wèn)題的,但潛在的會(huì)帶來(lái)一些隱患。這很容易給人造成的一個(gè)錯(cuò)覺是,我對(duì)輸入的字符串已經(jīng)很有效的做過(guò)處理了,以后使用沒(méi)有什么問(wèn)題。這句話沒(méi)有錯(cuò),對(duì)字符串來(lái)說(shuō)這樣做也是很正確的,但是他扮演了一個(gè)不光彩的角色,試想一下,如果過(guò)濾后的字符串放進(jìn)了數(shù)據(jù)庫(kù),而后續(xù)的語(yǔ)句有直接拿出來(lái)使用的,這種對(duì)前面過(guò)濾的依賴性,是不是正確的呢?

  也許較好的做法應(yīng)該是,針對(duì)具體的情況來(lái)確定過(guò)濾的準(zhǔn)則。
  常見的輸入變量有三種:數(shù)字,字符串還有集合。對(duì)于數(shù)字型的輸入變量,簡(jiǎn)單調(diào)用一下判斷函數(shù)即可,見得到的代碼中,凡是檢查了這類變量的,幾乎都正確。對(duì)于字符串型的來(lái)說(shuō),基本上在插入到生成的SQL語(yǔ)句時(shí),前后都有單引號(hào),如果僅從破壞注入條件來(lái)看,把單引號(hào)替換成兩個(gè)單引號(hào)應(yīng)該問(wèn)題不大。同理的,如果是一個(gè)字符串的集合,也可以簡(jiǎn)單的用這種方法。而如果是數(shù)字的集合,情況可能稍微麻煩一點(diǎn),至少你得允許數(shù)字、逗號(hào)或許還有空格之類的符號(hào)在輸入中正常出現(xiàn),這樣子的過(guò)濾規(guī)則可能顯得復(fù)雜,不過(guò)你可以借鑒一下dvBBS6.1打過(guò)補(bǔ)丁后的版本,總的來(lái)說(shuō),對(duì)于已經(jīng)發(fā)現(xiàn)的過(guò)濾漏洞而言,他們還是補(bǔ)得比較好的。
  對(duì)于第二句話,至少現(xiàn)在不能說(shuō)它說(shuō)錯(cuò)的,我們留待后面解決。

二、獲取的數(shù)據(jù)值得信賴嗎?

  其實(shí)這樣子說(shuō)范圍顯得有點(diǎn)大,一下子涉及到很多方面,一個(gè)例子一個(gè)例子地舉來(lái)看好了。

  首先是關(guān)于選擇過(guò)濾數(shù)據(jù)的問(wèn)題。一直以來(lái),我們認(rèn)為凡是用戶輸入的東西,都要經(jīng)過(guò)適當(dāng)?shù)奶幚。沒(méi)錯(cuò),但真正的是否都做到呢?隨便找個(gè)抓包的工具,比如Ethereal,看看在你用IE提交表單或者是打開連接的時(shí)候,都提交了什么;蛘,簡(jiǎn)單一些,打開NetAnt編輯一個(gè)任務(wù),在協(xié)議標(biāo)簽中,看看那個(gè)“自定義提交者”和“用戶代理”的選項(xiàng)。
  我想你已經(jīng)明白了,對(duì)方可以自己定制的東西不僅僅是GET或POST過(guò)來(lái)的數(shù)據(jù)!如果所有的用戶都規(guī)規(guī)矩矩地用瀏覽器,確實(shí)不用防備這么嚴(yán),如果對(duì)方不這么老實(shí),在取服務(wù)端變量或Cookie的時(shí)候可要小心了,沒(méi)有任何人能夠保證你獲得的數(shù)據(jù)是合法的。對(duì)于Cookie而言,很多程序都出過(guò)問(wèn)題,所以以前強(qiáng)調(diào)得比較多,至于另外的,關(guān)注的人可能比較少一點(diǎn),但你是否看過(guò)或者寫過(guò)這樣的代碼:

sql="ShowHOT_COM_inst_online_char 2,"&statuserid&",'"&membername&"','"&memberclass&"','"&Request.ServerVariables("REMOTE_HOST")&"',"&boardid&",'"&Request.ServerVariables("HTTP_USER_AGENT")&"','"&replace(stats,"'","")&"','"&Request.ServerVariables("HTTP_X_FORWARDED_FOR")&"',"&UserGroupID&",'"&actCome&"',"&userhidden&","&userid&""

  Request.ServerVariables("HTTP_USER_AGENT")就是你在NetAnt中看到的用戶代理選項(xiàng),也就是說(shuō)你可以偽造,同樣可以偽造的還有Request.ServerVariables("HTTP_REFERER"),也就是你在NetAnt中看到的提交者選項(xiàng)等等。在做一些項(xiàng)目的時(shí)候,很有可能要將這一類的變量添加入數(shù)據(jù)庫(kù),這時(shí)候要千萬(wàn)小心,這個(gè)地方的忽略,引起的后果和其他類型變量未過(guò)濾導(dǎo)致的后果是一樣的。
  在Google上搜索Referer和Request.ServerVariables兩個(gè)關(guān)鍵字,還可以看到很多有問(wèn)題的寫法,或者去看看五月份左右的關(guān)于動(dòng)網(wǎng)論壇入侵的文章,也許你的理解會(huì)更加深刻一點(diǎn)。

  然后是一個(gè)隱藏得稍微深一點(diǎn)的問(wèn)題,不是用戶的直接輸入要不要過(guò)濾?
  這就回到了我們前面留下的那個(gè)問(wèn)題,單引號(hào)換成兩個(gè)單引號(hào)的潛在威脅。在第二次構(gòu)造SQL語(yǔ)句的時(shí)候,倘若數(shù)據(jù)是從數(shù)據(jù)庫(kù)里面直接去取出來(lái)用的,多數(shù)情況下人們會(huì)認(rèn)為前面已經(jīng)處理過(guò)的東西看起來(lái)似乎并沒(méi)有必要再處理,或者干脆就是沒(méi)有意識(shí)到應(yīng)該處理。這是極其錯(cuò)誤的!從兩個(gè)方面來(lái)看,首先你入庫(kù)的時(shí)候?qū)μ峤粩?shù)據(jù)中的單引號(hào)處理,僅僅是保證了單次SQL語(yǔ)句構(gòu)造的正確性,并沒(méi)有一勞永逸地解決問(wèn)題;再說(shuō)了,后面取出數(shù)據(jù)用的時(shí)候,對(duì)數(shù)據(jù)安全性檢查的依賴并沒(méi)有得到保證,因?yàn)檫@種依賴關(guān)系沒(méi)有傳遞下來(lái),而且依賴關(guān)系本身還不是可傳的。
  就replace(request("someinput"), "'", "''")而言,它的不安定性在于這種過(guò)濾方式只是一種妥協(xié),換句話說(shuō)只是在有限的范圍內(nèi)掩蓋了可能出現(xiàn)的問(wèn)題,而沒(méi)有永久性的處理掉。它還有一個(gè)討厭的地方在于給人一種錯(cuò)覺,似乎是處理過(guò)的數(shù)據(jù)已經(jīng)安全了,容易讓后繼的代碼編寫者產(chǎn)生虛幻的安全感。對(duì)這兩個(gè)弱點(diǎn),不是靠換一個(gè)寫法就能解決的,因?yàn)槿绻惆褑我?hào)干脆去掉,又會(huì)引來(lái)另外一個(gè)問(wèn)題,輸入數(shù)據(jù)中確實(shí)有需要而且正確的單引號(hào)怎么辦?從一開始我就說(shuō),單引號(hào)本身是無(wú)罪的,過(guò)濾它只是一種解決手段而已,所以我們還是就這樣寫吧,不過(guò)要在后繼的部分加強(qiáng)一下檢查。
  這一類的問(wèn)題,如果依然用動(dòng)網(wǎng)論壇做例子,我建議看一下六月八號(hào)的漏洞文章。

  還有就是過(guò)濾器的位置,這個(gè)摻雜了邏輯問(wèn)題在內(nèi)的復(fù)雜問(wèn)題。
  我曾經(jīng)非常驚奇地發(fā)現(xiàn)喬客論壇對(duì)外散布的版本中一段讓人覺得不可思議的問(wèn)題代碼,如果你比較感興趣的話,翻翻gallery.asp就能看到一個(gè)特定的動(dòng)作序列(action=flash_view),繞過(guò)了所有對(duì)id的檢查。
  其實(shí)說(shuō)起來(lái),這一類代碼不太可能有太復(fù)雜的邏輯結(jié)構(gòu),對(duì)代碼進(jìn)行審查的時(shí)候,進(jìn)行所有的分支覆蓋是可以手工完成的,只要稍微想想就會(huì)發(fā)現(xiàn)對(duì)變量的檢查是否能夠有效地到達(dá)你的目的地——生成SQL語(yǔ)句的地方。
  關(guān)于過(guò)濾器的位置,如果要深入下去,馬上就會(huì)出來(lái)一些讓人眼花繚亂的東西,中間的分析很麻煩而且很形式化,雖然確實(shí)有算法可以保證位置選取的正確性,但是我想這里還是給出一些結(jié)論性的東西吧。倘若你很有興趣,我想你可以來(lái)信和我交流。
  過(guò)濾的位置,取決于兩個(gè)方面:你獲得變量的來(lái)源,以及你需要保證到的生成SQL語(yǔ)句的位置。前面一個(gè),不論是來(lái)自于直接還是間接輸入,先想想可能的輸入字符;對(duì)于后面一個(gè),你要保證無(wú)論程序運(yùn)行情況怎樣,經(jīng)過(guò)了過(guò)濾語(yǔ)句的流程一定會(huì)經(jīng)過(guò)你需要保證到的生成SQL語(yǔ)句的位置(保證其是有效過(guò)濾語(yǔ)句的后向必經(jīng)節(jié)點(diǎn))。如果你不很清楚流程的判斷,我的建議是if中僅僅判斷,if嵌套間不要有多余的東西,過(guò)濾語(yǔ)句后緊接生成SQL語(yǔ)句。
  再回到前面提到的潛在問(wèn)題,我們終于可以在這里解決了:在取出數(shù)據(jù)后依然首先進(jìn)行判斷。因?yàn)楦鶕?jù)前面說(shuō)的,這一種間接輸入依然有可能出現(xiàn)危險(xiǎn)。
  說(shuō)到這里,插一句另類的過(guò)濾位置問(wèn)題:不要把對(duì)輸入的過(guò)濾放到客戶端解決,那是可以繞過(guò)的!誰(shuí)能保證你的VBScript/JavaScript能起作用,如果別人直接用NC或者一個(gè)不支持腳本的瀏覽器呢?

  上述兩個(gè)大的方面,以軟件測(cè)試的目光來(lái)認(rèn)識(shí),顯然是沒(méi)有窮盡所有的分支所導(dǎo)致。在使用對(duì)方提交的數(shù)據(jù)之前,先做一個(gè)對(duì)方所有可能進(jìn)入字符的分析列表,然后就每一種輸入分支情況進(jìn)行類型的審核,這是每個(gè)代碼編寫者都應(yīng)該做的事情。這是一件很簡(jiǎn)單的事情,因?yàn)橹皇穷愋蜕系膶徍诉好,碰上語(yǔ)義的問(wèn)題就麻煩了……

三、類型正確意味著放行?

  涉及到語(yǔ)義的問(wèn)題,要是可能的話,我選擇最好還是避開。
  譬如對(duì)于一個(gè)整型數(shù)字,你輸入的確實(shí)是一個(gè)整型,通過(guò)了過(guò)濾器,潛在的問(wèn)題是你的輸入內(nèi)容上合法嗎,或者根本就不應(yīng)該從你這里獲得信息?很多年前就有人提出來(lái),有些注冊(cè)的模塊存在問(wèn)題:它里面的id是通過(guò)一個(gè)type=hidden掩蓋后隱式提交的,但是我在第一步建立了用戶,第二步仍就有可能通過(guò)提交內(nèi)容不合法的id來(lái)修改他人的信息。這種異類的問(wèn)題都是非常難發(fā)現(xiàn),而且?guī)缀醵贾挥锌拷?jīng)驗(yàn)而不是某一個(gè)具體的算法來(lái)處理。我們?cè)诼?lián)系一下前面的,連起來(lái)想想或許能夠更加清楚,對(duì)于輸入的字符串,感覺上沒(méi)有過(guò)濾也不會(huì)有錯(cuò),因?yàn)楸容^數(shù)字之類集合來(lái)說(shuō),字符串所能容納的幾乎是全部可能輸入的集合。事實(shí)上,常見的是沒(méi)有過(guò)濾造成單引號(hào)的錯(cuò)誤匹配,進(jìn)而導(dǎo)致了SQL Injection。嚴(yán)格說(shuō)起來(lái),這也是一個(gè)語(yǔ)義上的問(wèn)題,不過(guò)對(duì)于這樣子的特殊情況而言,可以通過(guò)處理輸入中的單引號(hào)來(lái)保證語(yǔ)義的某種程度上的正確。所以我也一再?gòu)?qiáng)調(diào),單引號(hào)本身是無(wú)罪的,不過(guò)是背了語(yǔ)義的黑鍋而已。
  令人遺憾的是,如果是整型數(shù)據(jù)出了語(yǔ)義上的問(wèn)題,沒(méi)有什么東西可以替語(yǔ)義背黑鍋了,所以沒(méi)有了一個(gè)一定程度上通用的解決方案。不過(guò)也不要悲觀,前面就已經(jīng)說(shuō)過(guò),能避開就避開,釜底抽薪不要讓可能有語(yǔ)義問(wèn)題的變量作為輸入好了。
  僅僅考慮數(shù)據(jù)庫(kù)安全的話,所有有威脅的語(yǔ)義問(wèn)題都幾乎出在對(duì)數(shù)據(jù)庫(kù)的操作上,那么,我們只要注意update/insert等語(yǔ)句就可以了,如果考慮數(shù)據(jù)內(nèi)容的安全性的話,select也得算上。一般來(lái)說(shuō),特別關(guān)注的是生成的where后面的條件語(yǔ)句,總覺得條件的語(yǔ)義應(yīng)該是由服務(wù)器端決定的,而不是說(shuō)用戶的輸入是什么就是什么。我的建議是對(duì)于所有的可能出現(xiàn)語(yǔ)義問(wèn)題的整型變量,最好都是Session,當(dāng)然,沒(méi)有進(jìn)行非常深入的研究,或許有人能夠提出像對(duì)付字符串的語(yǔ)義問(wèn)題一樣的有效方法也說(shuō)不一定。不過(guò)話又說(shuō)回來(lái),在語(yǔ)義層面上看對(duì)字符串的過(guò)濾,不能證明它不安全,但是更重要的沒(méi)有人能夠證明它安全,只是大家現(xiàn)在用著沒(méi)有問(wèn)題,也就默認(rèn)了罷了。
  若要深入的分析語(yǔ)義,也會(huì)突然冒出一大堆奇怪的東西,所以還是就此打住吧,真切的希望同行之間能夠多一些這方面的交流!

  前面說(shuō)的也許更多地會(huì)用在一些對(duì)既有代碼的補(bǔ)救上,如果是從頭開始構(gòu)架一個(gè)軟件的話,上面的僅僅是設(shè)計(jì)上一些參考。所有的漏洞都是源于設(shè)計(jì)上的缺陷,一個(gè)好的軟件應(yīng)該被證明其模型是正確的,這很難但是可以做到。如果你一開始就證明了軟件的正確性,我想也不會(huì)有漏子可以給別人鉆了。



標(biāo)簽:編寫安全的ASP代碼