案例 #1 味道聞起來很糟糕。兩個多月以來,數十萬加侖的污水流到了澳大利亞的公園、河流以及一家旅館的地面上,而無人知曉其原因。水生動植物不斷地死亡,而且有一條小河中的水已經變黑了。2000 年 4 月 23 日,當警方逮捕了一名男子時才解開了這個謎團,這
案例 #1 味道聞起來很糟糕。兩個多月以來,數十萬加侖的污水流到了澳大利亞的公園、河流以及一家旅館的地面上,而無人知曉其原因。水生動植物不斷地死亡,而且有一條小河中的水已經變黑了。2000 年 4 月 23 日,當警方逮捕了一名男子時才解開了這個謎團,這名男子一直在使用計算機和無線電設備對管理污水和飲用水的機器進行完全控制。他的動機是什麼呢?對其審訊的口供表明他正試圖獲得一份獲利豐厚的咨詢合同以解決他所造成的問題。事實原本還要糟糕得多。
案例 #2 有一個小偷(只知道他叫“Maxus”)從在線音樂公司 CD Universe 偷了 35 萬個信用卡號,然後勒索 10 萬美元贖金。當 CD Universe 拒絕支付贖金時,Maxus 就公開張貼了這些信用卡號 - 這損害了 CD Universe 的顧客並使這些顧客有合理的理由轉而光顧其它商店。
案例 #3 CIA 最近了解到奧薩馬·本·拉登的基地恐怖分子組織對網絡恐怖活動有著“非常濃厚的興趣”,這是人們先前所不相信的。鏈接到基地組織的計算機都可以獲得各種計算機“破解”工具,旨在引起災難性的破壞。
它們正在攻擊您的程序 - 您准備好了嗎? 計算機攻擊已成為一個非常嚴重的問題。1997 年,CERT/CC 報告發生了 2134 起計算機安全性事件,並報告了 311 個截然不同的安全性漏洞;到 2002 年,所報告的計算機安全性事件已上升至 82094 起,而安全性漏洞則上升到 4129 個。計算機安全性協會(Computer Security Institute,CSI)和舊金山聯邦調查局(Federal Bureau of Investigation,FBI)計算機入侵小組於 2003 年調查了 503 家大型公司和政府機構,發現其中有 92% 的被調查者都報告受到過攻擊。這些被調查者中,有 78% 確認他們的因特網連接經常受到攻擊,並有 36% 確認他們的內部系統經常受到攻擊。有 75% 的被調查者承認他們受到了經濟損失,盡管只有 47% 的被調查者可以確定他們損失的具體數目;但這些可以確定具體數目的組織的損失超過了 2 億美元。
攻擊持續升溫的原因有許多。計算機在不斷地聯網,這使攻擊者能比較容易地攻擊世界范圍內任何聯網的計算機,而不會有什麼風險。計算機已經非常普及;它們現在控制著比較多的具有價值的東西(這使它們值得進行攻擊)。過去,顧客十分願意購買不安全的軟件,所以根本沒人願意出錢
開發安全的軟件。
電子世界現在是一個危險程度更加高的地方。今天,我們要求幾乎所有的應用程序都是安全的應用程序。例如,實際上我們要求每個 Web 應用程序都是安全的應用程序,因為不可信的用戶可能向它們發送數據。甚至那些顯示或編輯本地文件的應用程序(如字處理器)都必須受到保護,因為有時用戶將顯示或編輯以電子郵件方式發送給他們的數據。
如果您在開發軟件,那麼您就是身處戰場,需要知道如何保護您自己。遺憾的是,大多數軟件開發人員從未知曉如何編寫安全的應用程序。
本專欄將幫助您了解如何編寫安全的應用程序。學校裡很少會教這類信息,其它地方也不太會講授這一主題。如果您學習了本專欄,那麼您將能夠保護您的程序,避免當前所用的最常見的攻擊。盡管我們主要討論 Linux 操作系統(也稱為 GNU/Linux),但是幾乎所有的內容都適用於任何類 UNIX 系統,而且其中的許多內容也適用於其它操作系統(象 Microsoft Windows)。
對於這第一篇文章,我將先介紹一些基礎
知識:安全性術語、改變您的理念、自由/開放源碼軟件(Free-Libre/open source software,FLOSS)的影響以及確定安全性需求。
術語:這些詞表示什麼意思? 每一領域都有其自己的術語,而計算機安全性領域中的術語則有些雜亂無章,其中充斥著首字母縮寫詞和易混淆的詞。以下幾個定義應該有所幫助:
攻擊者(attacker)(也稱為非法闖入者)是這樣的人:他試圖使程序或計算機做某些事情,而這些事情是明確禁止做的,如為了獲得或更改私有數據而闖入不屬於他們的計算機。
黑客(hacker)是計算機專家或計算機狂熱者。並非所有攻擊者都是黑客 - 有些攻擊者根本不了解計算機。同樣,並非所有黑客都是攻擊者 - 許多黑客編寫保護您的程序!媒體公司只關注那些攻擊計算機系統的黑客,而往往忽視那些計算機的保衛者,所以有些人就使用術語“黑客”表示只進行攻擊的黑客。但是,如果您認為所有的黑客都是攻擊者,那麼您在理解許多安全性文章時會碰到很多麻煩,所以我將使用這裡指出的定義。
缺陷(flaw)是程序中的錯誤,或是程序的安裝方法中的錯誤。並不是所有的缺陷都與安全性有關。
安全性漏洞(vulnerability)就是一個缺陷,它使程序有可能無法滿足其安全性需求。
漏洞利用(exloit)是一個揭示或利用安全性漏洞的程序。
改變您的理念 在學習如何編寫安全的軟件時遇到的最大挑戰是改變您對軟件開發的觀點。以下幾點應該會有所幫助:
偏執狂是一種優點。在自己進行調查研究之前不要相信任何事情。不要想當然地以為您的輸入遵守您所依賴的規則;去檢驗它。不要忽略來自庫的錯誤報告;通常,在遇到意想不到的錯誤時,您需要終止正在進行的處理。不要以為您的程序沒有錯誤;限制您的程序能夠完成的事情,這樣錯誤成為安全性缺陷的可能性就會比較少。
一般的
測試通常無法發現安全性缺陷。大多數
測試方法都假定用戶設法使用程序來幫助他們完成一些工作。因此,測試假定用戶將以某種“隨機”或“有用”的方式工作,檢查程序在“一般”情況下或者在某些最大值下如何工作。與此相反,安全性缺陷通常只出現在使用極其古怪的值的情況時,傳統的測試完全不會檢查這樣的值。有些開發人員會編寫非常糟糕的代碼,然後希望通過測試它來進行糾正。這種方法根本不會產生安全的代碼,因為您無法創建足夠多的測試來涵蓋攻擊者能做到的所有稀奇古怪的事情。
小玩意(象防火牆)和技術(象加密)是不夠的。
從過去的失敗確定和了解漏洞。事實證明幾乎所有軟件的安全性漏洞都是由相對較小的一組常見錯誤引起的。如果您了解了那些是什麼錯誤 - 以及如何避免這樣的錯誤 - 您的軟件就會安全得多。事實上,本專欄將集中討論如何避免以前出現的常見錯誤,這樣您就不會犯同樣的錯誤。
FLOSS 會使我們安全嗎?
自由/開放源碼軟件程序是帶有某種許可證的程序,這樣的許可證允許用戶以任何目的自由地運行程序,自由地研究和修改程序以及自由地重新分發原始程序副本或修改過的程序副本(而不必向以前的開發人員支付版稅)。FLOSS 的同義詞包括開放源碼軟件(OSS),大寫時是“自由軟件(Free Software)”(FS)以及 OSS/FS。“自由軟件”和“開放源碼軟件”可在本文中互換使用,但是推薦使用“FLOSS”,因為它包含了這兩個術語。典型的 FLOSS 程序都是由開發人員社區開發的,他們一起工作並評審彼此的工作。Linux 內核是 FLOSS,Apache Web
服務器和許多其它程序也是 FLOSS;FLOSS 正逐漸在許多市場特殊領域流行起來。
人們可以評審 FLOSS 程序的源代碼,這樣會如何影響安全性存在著激烈辯論。由於受到大眾所有可能的詳細審查,所以 FLOSS 會更安全嗎?或者,FLOSS 是否會更不安全,因為攻擊者獲得了更多的信息 - 這會使得進行對程序的攻擊更容易嗎?
這些問題開始有了答案,與象“FLOSS 總是比較安全的”這樣的簡單聲明相比,這些答案更為細致且更復雜。確實有事實證明 FLOSS 能比具有專利權的軟件安全。例如,據報告 FLOSS OpenBSD 操作系統的安全性漏洞比 Microsoft Windows 少得多。但是有一個合理的“反訴”:由於 Windows 用戶比較多,所以對 Windows 的攻擊就可能比較多,這意味著更有可能發現 Windows 的安全性漏洞。這就是比較的全部內容嗎?很值得懷疑,不過這也顯示出作同等對比是多麼困難。
一個更佳的示例是 Apache Web 服務器:它比 Microsoft 的具有專利權的 IIS Web 服務器流行得多,但是 Apache 的嚴重安全性漏洞卻比 IIS 少。請參閱我的論文“Why OSS/FS? Look at the Numbers”(在參考資料中)獲取有關 FLOSS 的更多統計信息,包括安全性統計信息。
還有一點很清楚,攻擊者實際上並不需要源代碼。只要研究可獲得的所有 Microsoft Windows 漏洞利用就可以了!更重要的是,如果攻擊者需要源代碼,那麼他們會使用反編譯器,來重新創建源代碼,這樣重新創建的源代碼對攻擊目的而言足夠了。
但是答案也並非就是“FLOSS 總是比較安全的”。畢竟,您可以將具有專利權的程序的許可證更改成 FLOSS,而不更改其代碼,而它不會突然就變得更安全。相反,有幾個因素看來是使 FLOSS 程序擁有良好的安全性所必不可少的:
必須多人真正地評審代碼。有多種因素會減少進行評審的可能性,如用於特殊領域或很少用到的產品(可能的評審者很少)、開發人員很少、使用罕見的計算機語言或實際上不是 FLOSS(如“共享源碼”許可證)。如果每次代碼更改都由許多開發人員進行檢查,這通常有助於安全性。
至少某些開發和評審代碼的人必須知道如何編寫安全的程序。一個人可以幫助
培訓其他人,但是您必須有一個起點。
一旦發現安全性漏洞,需要快速地開展修補工作並將修補成果進行分發。
簡而言之,程序是否安全最重要的因素是 - 不管它是 FLOSS 還是具有專利權的 - 其開發人員是否知道如何編寫安全的程序。
如果您需要安全的程序,那麼使用 FLOSS 程序就相當合理 - 但您需要以某種方式評估它以確定它對於您的目的是否足夠安全。
確定您的安全性需求 在您可以確定程序是否安全之前,需要先確切地確定其安全性需求是什麼。事實上,有關安全性的實際問題之一是安全性需求會根據不同的程序和不同的環境而迥然不同。文檔查看器或編輯器(如字處理器)可能需要確保查看數據不會使程序運行任意命令。購物車需要確保顧客不能自己定價,而且顧客不能查看有關其他顧客的信息等等。
事實上您可以使用一個國際標准來正式確定安全性需求並確定它們是否滿足要求。這個國際標准的正式標識符是 ISO/IEC 15408:1999,但人們都稱之為“通用標准(Common Criteria,CC)”。
有些合同特別要求您使用 CC 的所有細節,這樣的話,您需要知道的就要比本文所涉及的多得多。但對於許多情況,幫助您確定安全性需求所需的全部就是一個非正式的簡化方法。所以,我將根據 CC,來描述一個用於確定安全性需求的經過簡化的方法:
確定您的安全性環境。
確定您的安全性目標。
確定您的安全性需求。
即使您在非正式地這樣做,也請寫下您的結果 - 它們以後也可以向您和您的用戶提供幫助。
安全性環境 程序實際上不會在真空中工作 - 在某個環境中是安全的程序可能在另一個環境中就不安全了。因此,您必須確定您的程序要在什麼樣的環境(或多種環境)下工作。特別地,請考慮:
威脅。您的威脅有哪些? 誰會進行攻擊?可能的攻擊者也許包括無知的用戶、計算機業務愛好者、罪犯、對公司不滿的雇員、其他內部人員、沒有職業道德的競爭對手、恐怖分子的組織或甚至是外國政府。盡管攻擊者的危險程度會有不同,但是每個人都可能成為攻擊者的目標。設法確定您所信任的人;明確您不該信任其他任何人。確定誰是不可信的人是一個好主意,因為這會幫助您確定真正的問題是什麼。商業組織不能忽略恐怖分子或外國政府實施的電子攻擊 - 國家軍隊根本不會花費他們的資源來設法通過電子方式保護您。在電子世界中,我們所有的人都要靠我們自己,而且我們每個人都必須保護我們自己。
他們如何進行攻擊?您是否擔心會有特殊類型的攻擊,如攻擊者假扮合法用戶?有沒有那些在類似的程序中已經存在的安全性漏洞呢?
您正設法保護的是什麼資產?所有信息都是不相同的 - 您正設法保護的是哪幾種不同的信息,以及如何保護(防止被讀取?防止被更改?)?您擔心被竊取、破壞或被狡猾地修改嗎?根據您正設法保護的資產以及攻擊者可能會如何處理這些資產來考慮安全性。
假設。您需要做怎樣的假設?例如,您的系統是否受到保護而免受物理威脅?您的支持環境(平台和網絡)是什麼 - 它們安全嗎?
組織安全性策略。有沒有希望程序遵守或實現的法規或法律呢?例如,美國或歐洲的醫療系統必須(在法律上)使某些醫療數據保密。
安全性目標 一旦您知道了身處什麼環境;就可以確定您的安全性目標,它們基本上是高級需求。典型的安全性目標涉及多個方面,比如:
機密性:系統將防止在未授權情況下洩露信息(“不能讀”)。
完整性:系統將防止在未授權情況下更改信息(“不能更改”)。
可用性:系統將持續工作,即使在受攻擊的時候也是如此(“工作的持續性”)。能夠在所有可能的攻擊下繼續工作的系統是不存在的,但是系統可以抵御許多攻擊或在受攻擊後迅速恢復至可用狀態。
認證:系統將確保用戶是可信的。
審計:系統將記錄重要事件,以允許稍後跟蹤所發生的事情(例如,捕獲攻擊者或對攻擊者提起訴訟)。
通常,在您確定了安全性目標時,您的程序單靠自己還不能完成某些事情。例如,可能您正在運行的操作系統需要進行加強,或者可能您要依靠一些外部認證系統。這樣的話,您就需要確定這些環境需求並確保您告訴了用戶如何使這些需求成為現實。然後您可以將精力集中在您程序的安全性需求上。
功能性需求和保證需求 一旦知道了程序的安全性目標,就可以通過更詳細地填充其內容來確定安全性需求。CC 確定了兩類主要的安全性需求:保證需求和功能性需求。事實上,CC 的大多數內容是一個可能的保證需求和功能性需求的列表,您可以針對給定的程序從中進行選取。
保證需求是用來確保程序完成它該做的事情 - 而不做別的事 - 的過程。這可能包括評審程序文檔以查看其前後的一致性、測試安全性機制以確保它們按計劃工作,或者創建並運行滲透測試(為設法攻破程序而專門設計的測試)。CC 預先創建了幾組保證需求,但是如果其它保證措施有助於您滿足要求,那麼也可以自由使用這些措施。例如,您可以使用工具在您的源代碼中搜索可能的安全性問題(這些工具稱為“源代碼掃描工具”) - 即使它不是 CC 中的特定保證需求。
功能性需求是程序為實現安全性目標所執行的功能。也許程序會檢查密碼以認證用戶,或者對數據加密以將它隱藏等等。通常,只有“授權”用戶可以完成某些操作 - 所以請考慮程序應該如何確定誰獲得了授權。
下一步是什麼? 那麼,一旦知道了程序必須做的事情,這就夠了嗎?不。只要大致閱讀已知的安全性漏洞列表(如
Bugtraq、CERT 或 CVE)就可以知道,當今的大多數安全性漏洞都是由相對小的一組常見實現錯誤引起的。這些錯誤沒有單一的標准術語,但是還是有常見的短語來描述它們,如“不能驗證輸入(failing to validate input)”、“緩沖區溢出(buffer overflow)”以及“競態狀態(race condition)”等等。遺憾的是,大多數開發人員對於這些常見錯誤是什麼沒有任何概念,而且他們重復著別人以前已經犯過的錯誤。
本專欄未來的文章將深入討論這些常見錯誤是什麼,而且更重要的是,如何避免犯這樣的錯誤。在許多情況下,避免這樣錯誤的方法是細致加簡單 - 但是如果您不知道如何避免錯誤,那麼就有可能重犯這樣的錯誤。
在本專欄中,我一般不會設法將多種不同的應用程序(如“Web 應用程序”、“基礎結構組件”、“本地應用程序”或“setuid 應用程序”)分門別類。原因是什麼呢?今天的應用程序正不斷相互聯系,它們由許多不同的部分組成。其結果是,您的“一個”應用程序可能有幾個不同的部分,每個部分都屬於不同的一類,這是十分可能的!但是,我認為更好的做法是,了解如何在任何情況下都能開發安全的應用程序,並隨後注意何時應用特定的指導原則。
接下來的一篇專欄文章將由討論如何驗證輸入開始。這比聽上去還要棘手。例如,我們將看到為什麼尋找不正確的輸入是一個錯誤,而且攻擊者如何能經常使用非法的負數(而不使用“-”字符)就可以潛入。以後的主題將包括避免緩沖區溢出、使特權最小化以及避免競態狀態。要通過好幾篇文章才能涵蓋常見的錯誤,但通過閱讀本專欄,您將能夠避免那些造成目前幾乎所有的軟件安全性漏洞的錯誤。