天天看點

WEB應用安全設計思想

[目錄]

1. 前言

2. 信任關系的劃分是安全設計的基礎

3. 通路控制是安全設計的核心

4. 資料與代碼分離的思想是安全設計的原則

5. 最佳實踐一:Secure By Default

6. 最佳實踐二:Unpredictable

7. 總結

8. 參考資料

一、前言

    我一直在思考的一個問題,就是安全問題的本質到底是什麼。我們見到過各種各樣的攻擊,也做過各種各樣的防禦方案。有的方案好,有的方案卻有缺陷。那麼好的方案好在哪裡,為什麼就能夠抵抗攻擊,到底什麼特性使得攻擊者的成本升高了,使得風險降低了。這中間是否有什麼共同的東西呢?

    經過一段時間的思考和觀察,我初步得出了一個結論:安全問題的本質是信任問題。

二、信任關系的劃分是安全設計的基礎

    安全問題的本質是信任問題。提到這個,不得不說一個信任域的概念。當系統信任某些單元時,由這些單元組成的一片區域可以稱之為信任域。在資料流圖或者是拓跋圖上,都可以用一個邊界把這個域給界定出來。我說的這個概念,是一個廣義的概念,任何存在信任關系的系統中,都可以存在信任域。

    比如一個機場,人們要登機,必須要先經過安檢,那麼過了安檢後,在候機廳候機,就可以把候機廳看做是一個信任域。因為對于機場來說,候機廳内的區域是可信的。而候機廳外的區域是不可信的。

    機場的安檢就是對跨越信任邊界的一個檢查。會檢查有沒有刀具,有沒有液體、打火機等。    那麼安全問題是怎麼發生的呢?首先是沒有合理的劃分信任域,或者是信任域比較混亂。其次就是信任邊界的檢查出現問題的時候。這些問題可以是檢查不夠充分,或者是檢查沒有覆寫到整個信任邊界。

    而這些問題導緻的結果,都是産生信任危機,也就産生安全問題了。

    對于傳統的記憶體攻擊來說,一個字元串超出了配置設定給它的指定空間長度,也可以看做是對信任域的破壞,或者是缺乏審計。

    是以信任域和信任邊界是非常重要的東西。在做安全方案的時候,首先就要依據資産等級,去劃分信任域和信任邊界。

    我們要知道我們到底要保護什麼東西,然後去分析有什麼途徑能夠達到這些要保護的信任域。   在圈子裡經常講的一個笑話就是,怎麼做到安全?撥網線最安全。首先,這是一個謬論,因為網線拔掉後,可用性會受到影響。安全方案應該盡可能的避免犧牲可用性為代價,應該是為業務和應用服務的。拔網線是一種舍本逐末的做法。

    其次,拔了網線真的就安全了嗎?

    我們把實體隔絕的系統看做是一片信任域,那麼它會信任什麼?如何與外界做資料互動?簡單的頭腦風暴一下,就可以知道,這樣的系統,可能會與外界發生資料互動的情況:

    1. U盤有可能拷貝資料

    2. 無線網卡有可能自動連接配接

    3. 可能有人為的手工操作

    那麼以上這三條,都是有可能穿越我們的信任邊界,産生資料流動的行為。原本實體隔絕就是為了不信任外界的一切,産生資料流動後,就可能破壞信任關系。    再回過頭來看上面的機場的案例,把客流量看做是資料流量,它将穿越一道信任邊界,進入候機廳這個信任域,是以機場有安檢,來專門檢查這個穿越信任邊界的資料。安檢就是機場的安全方案。

-tips--------------------------------------------------------------------------

    如果A信任B,或者A依賴于B,則B可以決定A的安全。常見的案例比如軟體中使用了第三方包,則第三方包可以決定A中相關資料的安全。

-------------------------------------------------------------------------------

    某些視訊播放軟體使用了很多第三方的庫來解析很多不同的視訊格式,當第三方庫出現安全問題時,則直接導緻這些視訊播放軟體也出現安全問題。

    是以安全域的劃分是安全方案的基礎,劃分了安全域後,才能比較有針對性的設計安全方案。

三、通路控制是安全設計的核心

    通路控制不僅僅包括權限。權限僅僅隻是通路控制的一部分。這裡我們通常所說的權限都是垂直權限控制,它一般是基于角色的(role based)。

    比如一個論壇裡面,有匿名使用者,他們可能看不了文章的内容。有普通使用者,他們能看文章的内容。有管理者,他們能删文章,能置頂文章。

    那麼匿名使用者、普通使用者、管理者就是三個不同的角色。

    我們的大部分通路控制系統,都是基于角色的。普通使用者沒辦法執行管理者的操作,因為通路控制系統會校驗使用者的角色,以決定他們是否有足夠的權限去執行一次通路。

    通路控制系統一般在整個系統中處于一個比較中心的位置,也隻有讓他處在一個中心的、關鍵的位置,才能保證每次通路都由它來控制。

    但是目前我們的大多數系統都僅僅是垂直權限控制,而對水準權限控制方面卻做的不太好。

    什麼是水準權限控制?

    這個概念是相對于垂直權限控制來說的。

    A與B都是同一個角色的普通使用者。A上傳了一個頭像,系統給它編号為123,正常情況下,

    A可以執行“http://www.gesong.org /delete?id=123”去删除自己的頭像。

    但是由于這個删除操作僅僅校驗了使用者的角色,而沒有校驗送出該請求的使用者是否是A,進而導緻B可以送出以上請求,去删除A的頭像。

    這就是一個典型的水準權限控制出錯的例子。

    而很多系統中,同一個角色的使用者可以加入不同的使用者組,這些一個個的使用者組,就是一個水準權限控制的系統。

    隻是問題往往出在通路控制系統的粒度上。如果劃分的粒度不夠細,那麼一個使用者組内的使用者是否可以删除或修改各自的資料?

    對于粒度的劃分,我把一個通路控制系統中的最小機關稱之為一個原子權限。無論是水準權限系統還是垂直權限系統,可能都是對原子權限的不同組合。

  這個問題實際上是一個非常難以解決的問題,特别是在已經成型的大型系統中。對于現在的大型網際網路公司來說,網站的代碼一般都是幾十G的數量級,業務系統繁多。而水準權限控制的一般要求是,将所操作的資料與使用者聯系起來。

    回到上面的例子:delete?id=123

    那麼怎麼知道123這條資料,是A的呢?系統無從判斷,隻能去查詢user表。如果業務系統一複雜,可能就涉及到跨表查詢或者是聯合查詢,甚至是跨庫查詢,這基本上是一場噩夢。

    可是如果不進行二次查詢,則無法在根本的地方解決這個問題。可是二次查詢又會帶來性能上的消耗。真是一個很沖突的事情。

    是以最好的做法是在設計資料層的時候,事先考慮好這個問題,做好資料與使用者之間的關聯性。

    如果已經成型的系統,就隻能在外面包一層,把這個問題隐藏起來了。在本文的後面,會提到這種做法。

    除了水準和垂直權限控制外,實際上一些規則,也可以看做是通路控制。比如浏覽器裡的SOP(same original policy)。DOM、cookie等都有同源政策,也略有差别。但這些規則,都是屬于通路控制系統,在整個安全體系中,處于核心的位置。

    通路控制系統一般會針對資料的RWX(讀、寫、執行)屬性進行授權,對發起請求方則進行水準或垂直的檢查。

    而在WEB中,極其理想的狀态,可以大膽的想象為,以session為機關建立原子權限,将資料與session關聯起來後,每個不同的session就是不同的信任域,對每個跨越信任邊界的請求進行水準、垂直的權限檢查,這樣就是一個極端理想的權限體系。

 這隻是一個理想模型,在實踐中,需要根據實際情況進行分析。

四、資料與代碼分離的思想是安全設計的原則

    最典型的展現資料與代碼分離思想的是模闆系統。

    比如velocity,在渲染html的時候,程式員可以寫vm模闆,一些靜态寫死的内容就是代碼,而通過變量,經過渲染才最終展現的内容則稱之為資料。一個典型的例子如下:

    代碼與資料如果沒有分離,就會導緻代碼混亂,資料變成代碼的一部分去執行。比較常見的例子就是PHP裡的SQL寫法:

    如果參數 id 中帶有單引号,就會閉合掉代碼中的單引号,進而導緻資料變成代碼執行。是以這個注射的本質問題還是沒有做好資料與代碼的分離。

    比較好的做法是如下java代碼中的使用變量綁定,很好的做到了代碼與資料分離

    但是并不是說使用了模闆系統就一定分離了資料與代碼。

    因為在類似“render”或者是“transform”的過程中,往往存在一個将資料進行規範化的過程。這個過程也可能出現問題,進而導緻代碼可以混淆資料進行執行。

    比較好的做法是,資料中不能包含有在代碼中存在語義的字元。

    參考如下例子:

    紅字部分是使用者的輸入。

    在HTTP的标準中,冒号“:”,等号“=”,換行符CRLF“\r\n”,百分号“%”等字元都是有具體的語義的,屬于代碼部分。是以正常的使用者資料中不應該包含有這些字元。

    如果出于需求一定要包含怎麼辦?按照标準将這些字元全部encode。

    在HTTP标準中可以使用urlencode,比如等号就變成了“%3d”。

    這樣就做到了代碼與資料的分離。

    代碼與資料分離原則的本質還是展現了安全問題是信任問題這一思想。

    代碼是否應該信任資料,或者說代碼應該信任怎樣的資料,是這個原則的本質。

    在應用中,比較好的例子是json、XSLT,這些方法都比較好的做到了資料與代碼分離,是以在開發中多使用這些比較好的方法,無形中就提高了安全性。

五、最佳實踐一:Secure By Default

    經常可以看到一些權威文檔上推薦使用“default denied”,這就是“Secure By Default”的一種展現。

    “Secure By Default”可以說是一個最佳實踐。在很多時候,這個思想應該上升到戰略的高度。隻有真正做到“Secure By Default”,才能保證網站的安全。

    因為随着時間的推移和系統的發展、膨脹,會變得越來越臃腫。一個大系統發展到後期,基本上沒有一個人能了解系統的全部,而變化卻每天都在發生。是以,在這種情況下,隻有使用“Secure By Default”的思想來制定安全方案。

    白名單往往是實作“Secure By Default”的方法。與黑名單不同,白名單的思想很好的展現了“default denied”。下面以XSS的防禦問題舉例。

    對于一些HTML的标簽和事件,黑名單的做法是列出危險的标簽和事件,然後禁止他們。比如列出<script>、<iframe>等标簽,然後删除他們。

    而白名單的做法是留下允許的,比如<a>、<img>其餘的一律删除。對于白名單的做法來說,是可以抵禦未知攻擊的,而黑名單的做法則不行。

    2009年4月,firefox 3.1 beta3更新後,開始支援HTML5裡的新标準<audio>和<video>标簽,而某些基于黑名單的XSS filter沒有包含對這兩個新标簽的檢查,進而出現了漏洞。而類似的漏洞在一個基于白名單的系統中是不會存在的。

    一些基于異常行為檢測的IPS,也很好的展現了這種“Secure By Default”的思想。比如當FTP的使用者名或者密碼輸入非常長的時候,就認為這可能是一次攻擊,因為正常的使用者名和密碼是不會那麼長的。

    不過白名單也不是萬能的,如果白名單過于寬泛,則可能讓攻擊隐藏在白名單中,進而繞過的系統的檢查。

    回到安全的本質問題上,白名單屬于信任域,如果攻擊隐藏在信任域中,就屬于信任域不

合理。

    通配符“*”就是一個讓人很頭疼的東西,一定要慎用它!參考如下案例:

    Flash的crossdomain.xml檔案中,使用了通配符“*”,進而導緻這條安全配置形同虛設。

    是以,掌握好黑白名單的度,是一個重要的事情。

    一般選擇使用黑名單還是白名單,還需要考慮到工作量的問題,具體問題具體分析了。

六、最佳實踐二:Unpredictable

    Unpredictable可以了解為不可預測性。就是讓攻擊者無法預知它要攻擊的目标,把我們要保護的東西藏起來!

    不可預測性能夠有效的對抗基于篡改、僞造的攻擊。這些攻擊包括但不局限于 XSS、CSRF、Sql Injection、競争條件、通路控制問題等等。值得注意的是,Unpredictable僅僅隻是最佳實踐,但并不一定就是最好的方案。因為把問題藏起來,并非最終解決了問題,如果藏的不好,或者是有迹可循,也會導緻這些防禦方案失效。

    因為這種思想并非“在正确的地方做正确的事情”,在實踐中需要結合具體情況使用。  最典型的比如CSRF的防禦,目前比較通用的方案是增加一個随機的token,這個token的本質實際上就是為了讓攻擊者無法準确的預測到目标URL是什麼,進而也就讓僞造請求的攻擊無法成功的實施。

    但是如果通過XSS讀取了頁面裡的token,就使得攻擊者找到了隐藏的要素,進而使得僞造請求能夠成功的實施。在現代的緩沖區溢出的防禦中,ASLR(棧基址随機變化)也屬于不可預測性的一種展現。因為在記憶體攻擊中,往往需要知道一個确切的opcode位址才能讓程式按照攻擊者的意圖執行到shellcode,ASLR讓攻擊者難以找到一個确切的位址,進而 有效的防禦了一些記憶體攻擊。

    但是ASLR并沒有去從本質上解決程式中的緩沖區溢出問題。隻是把問題藏起來了。是以當攻擊者通過記憶體資訊洩露讀取到了記憶體中的資料,或者是找到了一個固定不變的位址時,就會讓這種保護變得無效。

    回到前面提到的水準權限控制問題,當我們很難去執行一個真正的解決方案時,就可以考慮使用增強方案來提供一種外部的保護。    

    在“Delete?id=123”中,攻擊者之是以能夠執行成功是因為它知道“id=123”,如果使用不可預測性的思想,讓攻擊者無法知道“id”是什麼,就能夠有效的對抗這種攻擊。

    當删除的操作為“delete?id=asfkjaskdjfneanef”時,攻擊者也就無法通過篡改id号,來實施越權通路了。

    這是另外一種解決水準權限問題的思路,僅做參考。

    總的來說,不可預測性是一種對安全防護的增強,可以錦上添花,但不能雪中送炭。

七、總結

    那麼,到底什麼才是一個好的安全方案?

    好的安全方案首先肯定是一個好的應用。

    好的應用有什麼特征?程式員可以說出許多:穩定性、易于維護、易于調試、可擴充性、高内聚,低耦合、高性能、良好的使用者體驗。

    這些也都應該是一個優秀的安全設計者在設計它的方案時需要考慮的問題。

    此外,一個好的安全設計,還要易于查找和發現安全問題,易于配置和審計。安全系統發展到一定階段,肯定會涉及到安全監控,一個好的安全設計,會有很大的幫助。

    以上,讨論了一些安全設計中的思想和原則,概括為:

* 信任關系的劃分是安全設計的基礎

* 通路控制系統是安全設計的核心

* 代碼與資料分離是安全設計的重要原則

* 最佳實踐一:Secure By Default

* 最佳實踐二:合理利用不可預測性

八、參考資料

<a href="http://cwe.mitre.org/top25/#CWE-285%20">http://cwe.mitre.org/top25/#CWE-285</a>

<a href="http://cwe.mitre.org/data/definitions/639.html%20">http://cwe.mitre.org/data/definitions/639.html</a>

<a href="http://cwe.mitre.org/data/definitions/566.html%20">http://cwe.mitre.org/data/definitions/566.html</a>

<a href="http://hi.baidu.com/aullik5/blog/item/342b4c4b6a20d82409f7eff2.html">http://hi.baidu.com/aullik5/blog/item/342b4c4b6a20d82409f7eff2.html</a>

-EOF-