關于軟件安全性的原則問題 軟件測試
原則 1:保護最薄弱的環節
安全性社區中最常見的比喻之一是:安全性是根鏈條;系統的安全程度只與最脆弱的環節一樣。結論是系統最薄弱部分就是最易受攻擊影響的部分。
攻擊者往往設法攻擊最易攻擊的環節,這對于您來說可能并不奇怪。如果他們無論因為什么原因將您的系統作為攻擊目標,那么他們將沿阻力最小的路線采取行動。這意味著他們將試圖攻擊系統中看起來最薄弱的部分,而不是看起來堅固的部分。即便他們在您系統各部分上花費相同的精力,他們也更可能在系統最需要改進的部分中發現問題。
這一直覺是廣泛適用的。銀行里的錢通常比便利店里的錢多,但是它們哪一個更易遭到搶劫呢?當然是便利店。為什么?因為銀行往往有更強大的安全性防范措施;便利店則是一個容易得多的目標。
讓我們假定您擁有一家普通的銀行和一家普通的便利店。是為保險庫添加額外的門并將安全人員的數目翻倍,還是為便利店花費同樣數目的錢雇傭安全官員更劃算呢?銀行可能已經將出納員置于防彈玻璃之后,并安裝了攝像機、配備了安全保衛、裝備了上鎖的保險庫以及具有電子密碼的門。相比之下,便利店可能裝備了沒那么復雜的攝像機系統以及很少的其它設備。如果您將對您的金融帝國的任何一部分進行安全性投資,那么便利店將是最佳選擇,因為它的風險要大得多。
這一原則顯然也適用于軟件世界,但大多數人并沒有給予任何重視。特別地,密碼術不太會是系統最薄弱的部分。即使使用具有 512 位 RSA 密鑰和 40 位 RC4 密鑰的 SSL-1,這種被認為是難以置信的薄弱的密碼術,攻擊者仍有可能找到容易得多的方法進入。的確,它是可攻破的,但是攻破它仍然需要大量的計算工作。
如果攻擊者想訪問通過網絡傳輸的數據,那么他們可能將其中一個端點作為目標,試圖找到諸如緩沖區溢出之類的缺陷,然后在數據加密之前或在數據解密之后查看數據。如果存在可利用的緩沖區溢出,那么世界上所有的密碼術都幫不了您 ― 而且緩沖區溢出大量出現在 C 代碼中。
因為這一原因,雖然加密密鑰長度的確對系統的安全性有影響,但在大多數系統中它們并不是如此的重要,在這些系統中更重要的事情都有錯。同樣地,攻擊者通常并不攻擊防火墻本身,除非防火墻上有眾所周知的弱點。實際上,他們將試圖突破通過防火墻可見的應用程序,因為這些應用程序通常是更容易的目標。
如果執行一個好的風險分析,則標識出您覺得是系統最薄弱的組件應該非常容易。您應該首先消除看起來好象是最嚴重的風險,而不是看起來最容易減輕的風險。一旦一些其它組件很明顯是更大的風險時,您就應該將精力集中到別的地方。
當然,可以永遠使用這一策略,因為安全性從來就不是一個保證。您需要某些停止點。根據您在軟件工程過程中定義的任何量度,在所有組件都似乎在可接受的風險閾值以內時,您應該停下來。
原則 2:縱深防御
縱深防御背后的思想是:使用多重防御策略來管理風險,以便在一層防御不夠時,在理想情況下,另一層防御將會阻止完全的破壞。即便是在安全性社區以外,這一原則也是眾所周知的;例如,這是編程語言設計的著名原則:
縱深防御:采取一系列防御,以便在一層防御不能抓住錯誤時,另一層防御將可能抓住它。
讓我們回到為銀行提供安全性的示例。為什么典型的銀行比典型的便利店更安全?因為有許多冗余的安全性措施保護銀行 ― 措施越多,它就越安全。單單安全攝像機通常就足以成為一種威懾。但如果攻擊者并不在乎這些攝像機,那么安全保衛就將在那兒實際保護銀行。兩名安全保衛甚至將提供更多的保護。但如果兩名保衛都被蒙面匪徒槍殺,那么至少還有一層防彈玻璃以及電子門鎖來保護銀行出納員。如果強盜碰巧砸開了這些門或者猜出了 PIN,起碼強盜將只能容易搶劫現金出納機,因為我們有保險庫來保護余下部分。理想情況下,保險庫由幾個鎖保護,沒有兩個很少同時在銀行的人在場是不能被打開的。至于現金出納機,可以為其裝備使鈔票留下印記的噴色裝置。
當然,配備所有這些安全性措施并不能確保銀行永遠不會遭到成功的搶劫。即便在具備這么多安全性的銀行,也確實會發生銀行搶劫。然而,很清楚,所有這些防御措施加起來會形成一個比任何單一防御措施有效得多的安全性系統。
這好象同先前的原則有些矛盾,因為我們實質上是在說:多重防御比最堅固的環節還要堅固。然而,這并不矛盾;“保護最薄弱環節”的原則適用于組件具有不重疊的安全性功能的時候。但當涉及到冗余的安全性措施時,所提供的整體保護比任意單個組件提供的保護要強得多,確實是可能的。
一個好的現實示例是保護在企業系統不同服務器組件間傳遞的數據,其中縱深防御會非常有用,但卻很少應用。大部分公司建立企業級的防火墻來阻止入侵者侵入。然后這些公司假定防火墻已經足夠,并且讓其應用程序服務器不受阻礙地同數據庫“交談”。如果數據非常重要,那么如果攻擊者設法穿透了防火墻會發生什么呢?如果對數據也進行了加密,那么攻擊者在不破解加密,或者(更可能是)侵入存儲未加密形式的數據的服務器之一的情況下,將不能獲取數據。如果我們正好在應用程序周圍建立另一道防火墻,我們就能夠保護我們免遭穿透了企業防火墻的人攻擊。那么他們就不得不在應用程序網絡顯式輸出的一些服務中尋找缺陷;我們要緊緊掌握那些信息。
原則 3:保護故障
任何十分復雜的系統都會有故障方式。這是很難避免的?梢员苊獾氖峭收嫌嘘P的安全性問題。問題是:許多系統以各種形式出現故障時,它們都歸結為不安全行為。在這樣的系統中,攻擊者只需造成恰當類型的故障,或者等待恰當類型的故障發生。
我們聽說過的最好的現實示例是將現實世界同電子世界連接起來的示例 ― 信用卡認證。諸如 Visa 和 MasterCard 這樣的大型信用卡公司在認證技術上花費巨資以防止信用卡欺詐。最明顯地,無論您什么時候去商店購物,供應商都會在連接到信用卡公司的設備上刷您的卡。信用卡公司檢查以確定該卡是否屬被盜。更令人驚訝的是,信用卡公司在您最近購物的環境下分析您的購物請求,并將該模式同您消費習慣的總體趨勢進行比較。如果其引擎察覺到任何十分值得懷疑的情況,它就會拒絕這筆交易。
從安全性觀點來看,這一方案給人的印象十分深刻 ― 直到您注意到某些事情出錯時所發生的情況。如果信用卡的磁條被去磁會怎樣呢?供應商會不得不說:“抱歉,因為磁條破了,您的卡無效!眴?不。信用卡公司還向供應商提供了創建您卡的標記的手工機器,供應商可以將其送給信用卡公司以便結帳。如果您有一張偷來的卡,那么可能根本不會進行認證。店主甚至可能不會向您要您的 ID。
在手工系統中一直有某些安全性所示,但現在沒了。在計算機網絡出現以前,可能會要您的 ID 以確保該卡同您的駕駛證相匹配。另外需要注意的是,如果您的號碼出現在當地定期更新的壞卡列表之內,那么該卡將被沒收。而且供應商還將可能核查您的簽名。電子系統一投入使用,這些技術實際上就再也不是必需的了。如果電子系統出現故障,那么在極少見的情況下,會重新使用這些技術。然而,實際不會使用這些技術。信用卡公司覺得:故障是信用卡系統中十分少見的情形,以致于不要求供應商在發生故障時記住復雜的過程。
系統出現故障時,系統的行為沒有通常的行為安全。遺憾的是,系統故障很容易引起。例如,很容易通過將偷來的信用卡在一塊大的磁鐵上掃一下來毀壞其磁條。這么做,只要小偷將卡用于小額購買(大額購買經常要求更好的驗證),他們就或多或少地生出了任意數目的金錢。從小偷的角度看,這一方案的優點是:故障很少會導致他們被抓獲。有人可以長期用這種方法使用同一張卡,幾乎沒有什么風險。
為什么信用卡公司使用這種愚蠢落后的方案呢?答案是:這些公司善于風險管理。只要他們能夠不停地大把賺錢,他們就可以承受相當大數量的欺詐。他們也知道阻止這種欺詐的成本是不值得的,因為實際發生的欺詐的數目相對較低。(包括成本和公關問題在內的許多因素影響這一決定。)
大量的其它例子出現在數字世界。經常因為需要支持不安全的舊版軟件而出現問題。例如,比方說,您軟件的原始版本十分“天真”,完全沒有使用加密,F在您想修正這一問題,但您已建立了廣大的用戶基礎。此外,您已部署了許多或許在長時間內都不會升級的服務器。更新更聰明的客戶機和服務器需要同未使用新協議更新的較舊的客戶機進行互操作。您希望強迫老用戶升級,但您尚未為此做準備。沒有指望老用戶會占用戶基礎中如此大的一部分,以致于無論如何這將真的很麻煩。怎么辦呢?讓客戶機和服務器檢查它從對方收到的第一條消息,然后從中確定發生了什么事情。如果我們在同一段舊的軟件“交談”,那么我們就不執行加密。
遺憾的是,老謀深算的黑客可以在數據經過網絡時,通過篡改數據來迫使兩臺新客戶機都認為對方是舊客戶機。更糟的是,在有了支持完全(雙向)向后兼容性的同時仍無法消除該問題。
對這一問題的一種較好解決方案是從開始就采用強制升級方案進行設計;使客戶機檢測到服務器不再支持它。如果客戶機可以安全地檢索到補丁,它就升級。否則,它告訴用戶他們必須手工獲得一個新的副本。很遺憾,重要的是從一開始就應準備使用這一解決方案,除非您不在乎得罪您的早期用戶。
遠程方法調用(Remote Method invocation (RMI))的大多數實現都有類似的問題。當客戶機和服務器想通過 RMI 通信,但服務器想使用 SSL 或一些其它加密協議時,客戶機可能不支持服務器想用的協議。若是這樣,客戶機通常會在運行時從服務器下載適當的套接字實現。這形成了一個大的安全漏洞,因為下載加密接口時,還沒有對服務器進行認證。攻擊者可以假裝成服務器,在每臺客戶機上安裝他自己的套接字實現,即使是在客戶機已經安裝了正確的 SSL 類的情況下。問題是:如果客戶機未能建立與缺省庫的安全連接(故障),它將使用一個不可信實體給它的任何協議建立連接,因此也就擴展了信任范圍。
原則 4:最小特權
最小特權原則規定:只授予執行操作所必需的最少訪問權,并且對于該訪問權只準許使用所需的最少時間。
當您給出了對系統某些部分的訪問權時,一般會出現濫用與那個訪問權相關的特權的風險。例如,我們假設您出去度假并把您家的鑰匙給了您的朋友,好讓他來喂養您的寵物、收集郵件等等。盡管您可能信任那位朋友,但總是存在這樣的可能:您的朋友未經您同意就在您的房子里開派對或發生其它您不喜歡的事情。
不管您是否信任您的朋友,一般不必冒險給予其必要的訪問權以外的權利。例如,如果您沒養寵物,只需要一位朋友偶爾收取您的郵件,那么您應當只給他郵箱鑰匙。即使您的朋友可能找到濫用那個特權的好方法,但至少您不必擔心出現其它濫用的可能性。如果您不必要地給出了房門鑰匙,那么所有一切都可能發生。
同樣,如果您在度假時確實雇傭了一位房子看管人,那么您不可能在沒有度假時還讓他保留您的鑰匙。如果您這樣做了,那么您使自己陷入額外的風險之中。只要當您的房門鑰匙不受您的控制,就存在鑰匙被復制的風險。如果有一把鑰匙不受您的控制,而且您不在家,那么就存在有人使用鑰匙進入您房子的風險。當有人拿了您的鑰匙,而您又沒有留意他們,那么任何這樣的一段時間都會構成一個時間漏洞,在此段時間內您就很容易受到攻擊。為了將您的風險降到最低,您要使這段易受攻擊的時間漏洞盡可能的短。
現實生活中的另一個好的示例是美國政府的忠誠調查系統 ―“需要知道”政策。即使您有權查看任何機密文檔,您仍不能看到您知道其存在的 任何機密文檔。如果可以的話,就很容易濫用該忠誠調查級別。實際上,人們只被允許訪問與那些交給他們的任務相關的文檔。
UNIX 系統中出現過一些違反最小特權原則的最著名情況。例如,在 UNIX 系統上,您一般需要 root 特權才能在小于 1024 的端口號上運行服務。所以,要在端口 25(傳統的 SMTP 端口)上運行郵件服務器,程序需要 root 用戶的特權。不過,一旦程序在端口 25 上運行了,就沒有強制性要求再對它使用 root 特權了。具有安全性意識的程序會放棄 root 特權并讓操作系統知道它不應再需要那些特權(至少在程序下一次運行之前)。某些電子郵件服務器中存在的一個大問題是它們在獲取郵件端口之后沒有放棄它們的 root 權限(Sendmail 是個經典示例)。因此,如果有人找到某種方法來欺騙這樣一個郵件服務器去完成某些惡意任務時,它會成功。例如,如果一位懷有惡意的攻擊者要在 Sendmail 中找到合適的棧溢出,則那個溢出可以用來欺騙程序去運行任意代碼。因為 Sendmail 在 root 權限之下運行,所以攻擊者進行的任何有效嘗試都會成功。
另一種常見情況是:一位程序員可能希望訪問某種數據對象,但只需要從該對象上進行讀。不過,不管出于什么原因,通常該程序員實際需要的不僅是必需的特權。通常,該程序員是在試圖使編程更容易一些。例如,他可能在想,“有一天,我可能需要寫這個對象,而我又討厭回過頭來更改這個請求!
不安全的缺省值在這里可能還會導致破壞。例如,在 Windows API 中有幾個用于訪問對象的調用,如果您將“0”作為參數傳遞,那么這些調用授予所有的訪問。為了更有限制地進行訪問,您需要傳遞一串標志(進行“OR”操作)。只要缺省值有效,許多程序員就會堅持只使用它,因為那樣做最簡單。
對于受限環境中運行的產品的安全性政策,這個問題開始成為其中的常見問題。例如,有些供應商提供作為 Java applet 運行的應用程序。applet 構成移動代碼,Web 瀏覽器會對此代碼存有戒心。這樣的代碼運行在沙箱中,applet 的行為根據用戶同意的安全性政策受到限制。在這里供應商幾乎不會實踐最小特權原則,因為他們那方面要花太多的精力。要實現大體意思為“讓供應商的代碼完成所有的任務”的策略相對要容易得多。人們通常采用供應商提供的安全性策略,可能是因為他們信任供應商,或者可能因為要確定什么樣的安全性策略能最佳地使必須給予供應商應用程序的特權最小化,實在是一場大爭論。
原則 5:分隔
果您的訪問權結構不是“完全訪問或根本不準訪問”,那么最小特權原則會非常有效。讓我們假設您在度假,而你需要一位寵物看管人。您希望看管人只能進出您的車庫(您不在時將寵物留在那里)但是如果您的車庫沒有一把單獨的鎖,那么您別無選擇而只能讓看管人進出整幢房子。
分隔背后的基本思想是如果我們將系統分成盡可能多的獨立單元,那么我們可以將對系統可能造成損害的量降到最低。當將潛水艇構造成擁有許多不同的船艙,每個船艙都是獨立密封,就應用了同樣原則;如果船體裂開了一個口子而導致一個船艙中充滿了水,其它船艙不受影響。船只的其余部分可以保持其完整性,人們就可以逃往潛水艇未進水的部分而幸免于難。
分隔原則的另一個常見示例是監獄,那里大批罪犯集中在一起的能力降到了最低。囚犯們不是居住在營房中,而是在單人或雙人牢房里。即使他們聚集在一起 ― 假定,在食堂里 ― 也可以加強其它安全性措施來協助控制人員大量增加帶來的風險。
在計算機世界里,要舉出糟糕分隔的示例比找出合理分隔容易得多。怎樣才能不分隔的經典示例是標準 UNIX 特權模型,其中安全性是關鍵的操作是以“完全訪問或根本不準訪問”為基礎的。如果您擁有 root 特權,那么您基本上可以執行您想要的任何操作。如果您沒有 root 訪問權,那么就會受到限制。例如,您在沒有 root 訪問權時不能綁定到 1024 以下的端口。同樣,您不能直接訪問許多操作系統資源 ― 例如,您必須通過一個設備驅動程序寫磁盤;您不能直接處理它。
通常,如果攻擊者利用了您代碼中的緩沖區溢出,那人就可以對磁盤進行原始寫并胡亂修改內核所在內存中的任何數據。沒有保護機制能阻止他這樣做。因此,您不能直接支持您本地磁盤上永遠不能被擦去的日志文件,這意味著直到攻擊者闖入時,您才不能保持精確的審計信息。不管驅動程序對底層設備的訪問協調得多么好,攻擊者總能夠避開您安裝的任何驅動程序。
在大多數平臺上,您不能只保護操作系統的一部分而不管其它部分。如果一部分不安全,那么整個系統都不安全。有幾個操作系統(諸如 Trusted Solaris)確實做了分隔。在這樣的情況中,操作系統功能被分解成一組角色。角色映射到系統中需要提供特殊功能的實體上。一個角色可能是 LogWriter 角色,它會映射到需要保存安全日志的任何客戶機上。這個角色與一組特權相關聯。例如,LogWriter 擁有附加到它自己的日志文件的權限,但決不可以從任何日志文件上進行擦除?赡苤挥幸粋特殊的實用程序獲得對 LogManager 角色的訪問,它就擁有對所有日志的完全訪問權。標準程序沒有對這個角色的訪問權。即使您破解了一個程序并在操作系統終止這個程序,您也不能胡亂修改日志文件,除非您碰巧還破解了日志管理程序。這種“可信的”操作系統并不是非常普遍,很大一部分是因為這種功能實現起來很困難。象在操作系統內部處理內存保護這樣的問題給我們提出了挑戰,這些挑戰是有解決方案的,但得出解決的結果并不容易。
分隔的使用必須適度,許多其它原則也是如此。如果您對每一個功能都進行分隔,那么您的系統將很難管理。
文章來源于領測軟件測試網 http://www.kjueaiud.com/