摘要:涉及安全問題時,有很多情況都會導(dǎo)致出現(xiàn)麻煩。您可能信任所有在您的網(wǎng)絡(luò)上運行的代碼,賦予所有用戶訪問重要文件的權(quán)限,并且從不費神檢查您機器上的代碼是否已經(jīng)改變。您也可能沒有安裝防病毒軟件,沒有給您自己的代碼建立安全機制,并賦予太多帳戶以太多的權(quán)限。您甚至可能非常大意地使用大量內(nèi)置函數(shù)從而允許惡意侵入,并且可能任憑服務(wù)器端口開著而沒有任何監(jiān)控措施。顯然,我們還可以舉出更多的例子。哪些是真正重要的問題(即,為了避免危及您的數(shù)據(jù)和系統(tǒng),應(yīng)立即予以關(guān)注的最危險的錯誤)?安全專家 Michael Howard 和 Keith Brown 提出了十條技巧來幫助您解脫困境。
1. 信任用戶的輸入會將自己置于險境
即使不閱讀余下的內(nèi)容,也要記住一點,“不要信任用戶輸入”。如果您總是假設(shè)數(shù)據(jù)是有效的并且沒有惡意,那么問題就來了。大多數(shù)安全薄弱環(huán)節(jié)都與攻擊者向服務(wù)器提供惡意編寫的數(shù)據(jù)有關(guān)。
信任輸入的正確性可能會導(dǎo)致緩沖區(qū)溢出、跨站點腳本攻擊、SQL 插入代碼攻擊等等。
讓我們詳細(xì)討論一下這些潛在攻擊方式。
2. 防止緩沖區(qū)溢出
當(dāng)攻擊者提供的數(shù)據(jù)長度大于應(yīng)用程序的預(yù)期時,便會發(fā)生緩沖區(qū)溢出,此時數(shù)據(jù)會溢出到內(nèi)部存儲器空間。緩沖區(qū)溢出主要是一個 C/C++ 問題。它們是種威脅,但通常很容易修補。我們只看到過兩個不明顯且難以修復(fù)的緩沖區(qū)溢出。開發(fā)人員沒有預(yù)料到外部提供的數(shù)據(jù)會比內(nèi)部緩沖區(qū)大。溢出導(dǎo)致了內(nèi)存中其他數(shù)據(jù)結(jié)構(gòu)的破壞,這種破壞通常會被攻擊者利用,以運行惡意代碼。數(shù)組索引錯誤也會造成緩沖區(qū)下溢和超限,但這種情況沒那么普遍。
請看以下 C++ 代碼片段:
void DoSomething(char *cBuffSrc, DWORD cbBuffSrc) {char cBuffDest[32];memcpy(cBuffDest,cBuffSrc,cbBuffSrc);}問題在哪里?事實上,如果 cBuffSrc 和 cbBuffSrc 來自可信賴的源(例如不信任數(shù)據(jù)并因此而驗證數(shù)據(jù)的有效性和大小的代碼),則這段代碼沒有任何問題。然而,如果數(shù)據(jù)來自不可信賴的源,也未得到驗證,那么攻擊者(不可信賴源)很容易就可以使 cBuffSrc 比 cBuffDest 大,同時也將 cbBuffSrc 設(shè)定為比 cBuffDest 大。當(dāng) memcpy 將數(shù)據(jù)復(fù)制到 cBuffDest 中時,來自 DoSomething 的返回地址就會被更改,因為 cBuffDest 在函數(shù)的堆??蚣苌吓c返回地址相鄰,此時攻擊者即可通過代碼執(zhí)行一些惡意操作。
彌補的方法就是不要信任用戶的輸入,并且不信任 cBuffSrc 和 cbBuffSrc 中攜帶的任何數(shù)據(jù):
void DoSomething(char *cBuffSrc, DWORD cbBuffSrc) {const DWORD cbBuffDest = 32;char cBuffDest[cbBuffDest];#ifdef _DEBUGmemset(cBuffDest, 0x33, cbBuffSrc);#endifmemcpy(cBuffDest, cBuffSrc, min(cbBuffDest, cbBuffSrc));}此函數(shù)展示了一個能夠減少緩沖區(qū)溢出的正確編寫的函數(shù)的三個特性。首先,它要求調(diào)用者提供緩沖區(qū)的長度。當(dāng)然,您不能盲目相信這個值!接下來,在一個調(diào)試版本中,代碼將探測緩沖區(qū)是否真的足夠大,以便能夠存放源緩沖區(qū)。如果不能,則可能觸發(fā)一個訪問沖突并把代碼載入調(diào)試器。在調(diào)試時,您會驚奇地發(fā)現(xiàn)竟有如此多的錯誤。最后也是最重要的是,對 memcpy 的調(diào)用是防御性的,它不會復(fù)制多于目標(biāo)緩沖區(qū)存放能力的數(shù)據(jù)。
在 Windows® Security Push at Microsoft(Microsoft Windows® 安全推動活動)中,我們?yōu)?C 程序員創(chuàng)建了一個安全字符串處理函數(shù)列表。您可以在 Strsafe.h: Safer String Handling in C(英文)中找到它們。
3. 防止跨站點腳本
跨站點腳本攻擊是 Web 特有的問題,它能通過單個 Web 頁中的一點隱患危害客戶端的數(shù)據(jù)。想像一下,下面的 ASP.NET 代碼片段會造成什么后果:
<script language=c#>Response.Write("您好," + Request.QueryString("name"));</script>有多少人曾經(jīng)見過類似的代碼?但令人驚訝的是它有問題!通常,用戶會使用類似如下的 URL 訪問這段代碼:
http://explorationair.com/welcome.aspx?name=Michael該 C# 代碼認(rèn)為數(shù)據(jù)始終是有效的,并且只是包含了一個名稱。但攻擊者會濫用這段代碼,將腳本和 HTML 代碼作為名稱提供。如果輸入如下的 URL
http://northwindtraders.com/welcome.aspx?name=<script>alert(‘您好!‘);</script>您將得到一個網(wǎng)頁,上面顯示一個對話框,顯示“您好!”。您可能會說,“那又怎樣?”想像一下,攻擊者可以誘導(dǎo)用戶點擊這樣的鏈接,但查詢字符串中卻包含一些真正危險的腳本和 HTML,由此會得到用戶的 cookie 并把它發(fā)送到攻擊者擁有的網(wǎng)站;現(xiàn)在攻擊者便獲得了您的私人 cookie 信息,或許會更糟。
要避免這種情況,有兩種方法。第一種是不信任輸入,并嚴(yán)格限制用戶名所包含的內(nèi)容。例如,可以使用正則表達(dá)式檢查該名稱是否只包含一個普通的字符子集,并且不太大。以下 C# 代碼片段顯示了完成這一步驟的方法:
Regex r = new Regex(@"^[\w]{1,40}$");if (r.Match(strName).Success) {// 好!字符串沒問題} else {// 不好!字符串無效}這段代碼使用正則表達(dá)式驗證一個字符串僅包含 1 到 40 個字母或數(shù)字。這是確定一個值是否正確的唯一安全方法。
HTML 或腳本不可能蒙混過此正則表達(dá)式!不要使用正則表達(dá)式尋找無效字符并在發(fā)現(xiàn)這種無效字符后拒絕請求,因為容易出現(xiàn)漏掉的情況。
第二種防范措施是對所有作為輸出的輸入進行 HTML 編碼。這會減少危險的 HTML 標(biāo)記,使之變成更安全的轉(zhuǎn)義符。您可以在 ASP.NET 中使用 HttpServerUtility.HtmlEncode,或者在 ASP 中使用 Server.HTMLEncode 轉(zhuǎn)義任何可能出現(xiàn)問題的字符串。
4. 不要請求 sa 權(quán)限
我們要討論的最后一種輸入信任攻擊是 SQL 插入代碼。許多開發(fā)人員編寫這樣的代碼,即獲取輸入并使用該輸入來建立 SQL 查詢,進而與后臺數(shù)據(jù)存儲(如 Microsoft® SQL Server™ 或 Oracle)進行通信。
請看以下代碼片段:
void DoQuery(string Id) {SqlConnection sql=new SqlConnection(@"data source=localhost;" +"user id=sa;password=password;");sql.Open();sqlstring= "SELECT hasshipped" +" FROM shipping WHERE id=‘" + Id + "‘";SqlCommand cmd = new SqlCommand(sqlstring,sql);•••這段代碼有三個嚴(yán)重缺陷。首先,它是以系統(tǒng)管理員帳戶 sa 建立從 Web 服務(wù)到 SQL Server 的連接的。不久您就會看到這樣做的缺陷所在。第二點,注意使用“password”作為 sa 帳戶密碼的聰明做法!
SELECT hasshipped FROM shipping WHERE id = ‘1001‘但攻擊者比這要有創(chuàng)意得多。他們會為 ID 輸入一個“‘1001‘ DROP table shipping --”,它將執(zhí)行如下查詢:
SELECT hasshipped FROMshipping WHERE id = ‘1001‘DROP table shipping -- ‘;它更改了查詢的工作方式。這段代碼不僅會嘗試判斷是否裝運了某些貨物,它還會繼續(xù) drop(刪除)shipping 表!操作符 -- 是 SQL 中的注釋操作符,它使攻擊者能夠更容易地構(gòu)造一系列有效但危險的 SQL 語句!
這時您也許會覺得奇怪,怎么任何一個用戶都能刪除 SQL Server 數(shù)據(jù)庫中的表呢。當(dāng)然,您是對的,只有管理員才能做這樣的工作。但這里您是作為 sa 連接到數(shù)據(jù)庫的,而 sa 能在 SQL Server 數(shù)據(jù)庫上做他想做的任何事。永遠(yuǎn)不要在任何應(yīng)用程序中以 sa 連接 SQL Server;正確的做法是,如果合適,使用 Windows 集成的身份驗證,或者以一個預(yù)先定義的具有適當(dāng)權(quán)限的帳戶連接。
修復(fù) SQL 插入代碼問題很容易。使用 SQL 存儲過程及參數(shù),下面的代碼展示了創(chuàng)建這種查詢的方法 - 以及如何使用正則表達(dá)式來確認(rèn)輸入有效,因為我們的交易規(guī)定貨運 ID 只能是 4 到 10 位數(shù)字:
Regex r = new Regex(@"^\d{4,10}$");if (!r.Match(Id).Success)throw new Exception("無效 ID");SqlConnection sqlConn= new SqlConnection(strConn);string str="sp_HasShipped";SqlCommand cmd = new SqlCommand(str,sqlConn);cmd.CommandType = CommandType.StoredProcedure;cmd.Parameters.Add("@ID",Id);緩沖區(qū)溢出、跨站點腳本和 SQL 插入代碼攻擊都是信任輸入問題的示例。所有這些攻擊都能通過一種機制來減輕危害,即認(rèn)為所有輸入都是有害的,除非獲得證明。
5. 注意加密代碼!
下面我們來看些會讓我們吃驚的東西。我發(fā)現(xiàn)我們檢查的安全代碼中百分之三十以上都存在安全漏洞。最常見的漏洞可能就是自己的加密代碼,這些代碼很可能不堪一擊。永遠(yuǎn)不要創(chuàng)建自己的加密代碼,那是徒勞的。不要認(rèn)為僅僅因為您有自己的加密算法其他人就無法破解。攻擊者能使用調(diào)試器,他們也有時間和知識來確認(rèn)系統(tǒng)如何工作 - 通常在幾小時內(nèi)就會破解它們。您應(yīng)該使用 Win32® 的 CryptoAPI,System.Security.Cryptography 命名空間提供了大量優(yōu)秀且經(jīng)過測試的加密算法。
6. 減少自己被攻擊的可能性
如果沒有百分之九十以上的用戶要求,則不應(yīng)默認(rèn)安裝某一功能。Internet Information Services (IIS) 6.0 遵循了這一安裝建議,您可以在這個月發(fā)布的 Wayne Berry 的文章“Innovations in Internet Information Services Let You Tightly Guard Secure Data and Server Processes”中讀到相關(guān)內(nèi)容。這種安裝策略背后的思想是您不會注意自己并未使用的服務(wù),如果這些服務(wù)正在運行,則可能被其他人利用。如果默認(rèn)安裝某功能,則它應(yīng)在最小授權(quán)原則下運行。也就是說,除非必要,否則不要允許使用管理員權(quán)限運行應(yīng)用程序。最好遵循這一忠告。
7. 使用最小授權(quán)原則
出于若干原因,操作系統(tǒng)和公共語言運行時有一個安全策略。很多人以為此安全策略存在的主要原因是防止用戶有意破壞:訪問他們無權(quán)訪問的文件、重新配置網(wǎng)絡(luò)以達(dá)到他們的要求以及其他惡劣行為。的確,這種來自內(nèi)部的攻擊很普遍,也需要防范,但還有另一個原因需要嚴(yán)守這一安全策略。即在代碼周圍建立起防范壁壘以防止用戶有意或(正如經(jīng)常發(fā)生的)無意的操作對網(wǎng)絡(luò)造成嚴(yán)重破壞。例如,通過電子郵件下載的附件在 Alice 的機器上執(zhí)行時被限制為只能訪問 Alice 可以訪問的資源。如果附件中含有特洛伊木馬,那么好的安全策略就是限制它所能產(chǎn)生的破壞。
當(dāng)您設(shè)計、建立并部署服務(wù)器應(yīng)用程序時,您不能假設(shè)所有請求都來自合法用戶。如果一個壞家伙發(fā)送給您一個惡意請求(但愿不會如此)并使您的代碼產(chǎn)生惡劣操作,您會希望您的應(yīng)用程序擁有所有可能的防護來限制損害。因此我們認(rèn)為,您的公司實施安全策略不僅是因為它不信任您或您的代碼,同時也是為了保護不受外界有企圖的代碼的傷害。
最小授權(quán)原則認(rèn)為,要在最少的時間內(nèi)授予代碼所需的最低權(quán)限。也就是說,任何時候都應(yīng)在您的代碼周圍豎起盡可能多的防護墻。當(dāng)發(fā)生某些不好的事情時 - 就象 Murphy 定律保證的那樣 - 您會很高興這些防護墻都處在合適的位置上。因此,這里就使用最小授權(quán)原則運行代碼給出了一些具體方法。
為您的服務(wù)器代碼選擇一個安全環(huán)境,僅允許其訪問完成其工作所必需的資源。如果您代碼中的某些部分要求很高的權(quán)限,請考慮將這部分代碼分離出來并單獨以較高的權(quán)限運行。為安全分離這一以不同的操作系統(tǒng)驗證信息運行的代碼,您最好在一個單獨的進程(運行在具有更高權(quán)限的安全環(huán)境中)中運行此代碼。這意味著您將需要進程間通訊(如 COM 或 Microsoft .NET 遠(yuǎn)程處理),并且需要設(shè)計該代碼的接口以使往返行程最小。
如果在 .NET Framework 環(huán)境中將代碼分離成程序集,請考慮每段代碼所需的權(quán)限級別。您會發(fā)現(xiàn)這是一個很容易的過程:把需要較高權(quán)限的代碼分離到可賦予其更多權(quán)限的單獨的程序集中,同時使其余大部分程序集以較低的權(quán)限運行,從而在您的代碼周圍添加更多的防護。 在進行此操作時,不要忘了,由于代碼訪問安全 (CAS) 堆棧的作用,您限制的不僅是自己程序集的權(quán)限,也包括您調(diào)用的任何程序集的權(quán)限。
許多人建立了自己的應(yīng)用程序,使得其產(chǎn)品在測試并提供給客戶后可以插入新的組件。保護這種類型的應(yīng)用程序非常困難,因為您無法測試所有可能的代碼路徑來發(fā)現(xiàn)錯誤和安全漏洞。然而,如果您的應(yīng)用程序是托管的,則 CLR 提供了一個極好的功能,可以使用它關(guān)閉這些可擴展點。通過聲明一個權(quán)限對象或一個權(quán)限集并調(diào)用 PermitOnly 或 Deny,您可以為自己的堆棧添加一個標(biāo)記,它將阻塞授予您調(diào)用的任何代碼的權(quán)限。通過在調(diào)用某個插件之前進行此操作,您就可以限制該插件所能執(zhí)行的任務(wù)。例如,一個用于計算分期付款的插件不需要任何訪問文件系統(tǒng)的權(quán)限。這只是最小權(quán)限的另一個例子,由此您可以事先保護自己。請確保記錄下這些限制,并注意,具有較高權(quán)限的插件能夠使用 Assert 語句逃避這些限制。
8. 注意失敗模式
接受它吧。其他人和您一樣憎恨編寫錯誤處理代碼。導(dǎo)致代碼失敗的原因如此眾多,一想到這些就讓人沮喪。大多數(shù)程序員,包括我們,更愿意關(guān)注正常的執(zhí)行路徑。那里才是真正完成工作的地方。讓我們盡可能快而無痛地完成這些錯誤處理,然后繼續(xù)下一行真正的代碼吧。
只可惜,這種情緒并不安全。相反,我們需要更密切地關(guān)注代碼中的失敗模式。人們對這些代碼的編寫通常很少深入注意,并且常常沒有經(jīng)過完全測試。還記得最后一次您完全肯定調(diào)試過函數(shù)的每一行代碼,包括其中每一個很小的錯誤處理程序是什么時候?
未經(jīng)測試的代碼常會導(dǎo)致安全漏洞。有三件事情可以幫助您減輕這個問題。首先,對那些很小的錯誤處理程序給予和正常代碼同樣的關(guān)注??紤]當(dāng)您的錯誤處理代碼執(zhí)行時系統(tǒng)的狀態(tài)。系統(tǒng)是否處于有效并且安全的狀態(tài)中?其次,一旦您編寫了一個函數(shù),請逐步將它徹底調(diào)試幾遍,確保測試每一個錯誤處理程序。注意,即使使用這樣的技術(shù),也可能無法發(fā)現(xiàn)非常隱秘的計時錯誤。您可能需要給您的函數(shù)傳遞錯誤參數(shù),或者以某種方式調(diào)整系統(tǒng)的狀態(tài),以使您的錯誤處理程序得以執(zhí)行。通過花時間單步調(diào)試代碼,您可以慢下來并有足夠的時間來查看代碼以及系統(tǒng)運行時的狀態(tài)。通過在調(diào)試器中仔細(xì)單步執(zhí)行代碼,我們在自己的編程邏輯中發(fā)現(xiàn)了許多缺陷。這是一個已得到證明的技術(shù)。請使用這一技術(shù)。最后,確保您的測試組合能使您的函數(shù)進行失敗測試。盡量使測試組合能夠檢驗函數(shù)中的每一行代碼。這能幫助您發(fā)現(xiàn)規(guī)律,特別是當(dāng)使測試自動化并在每次建立代碼后運行測試時。
關(guān)于失敗模式還有一件非常重要的事情需要說明。當(dāng)您的代碼失敗時要確保系統(tǒng)處于可能的最安全狀態(tài)。下面顯示了一些有問題的代碼:
bool accessGranted = true; // 過于樂觀!try {// 看看我們能否訪問 c:\test.txtnew FileStream(@"c:\test.txt",FileMode.Open,FileAccess.Read).Close();}catch (SecurityException x) {// 訪問被拒絕accessGranted = false;}catch (...) {// 發(fā)生了其他事情}盡管我們使用了 CLR,我們?nèi)员辉试S訪問該文件。在這種情況下,并沒有引發(fā)一個 SecurityException。但是,例如,如果文件的自由訪問控制列表 (DACL) 不允許我們訪問呢?這時,會引發(fā)另一種類型的異常。但由于代碼第一行的樂觀假設(shè),我們永遠(yuǎn)也不會知道這一點。
編寫這段代碼的一種更好的方法就是持謹(jǐn)慎態(tài)度:
bool accessGranted = false; // 保持謹(jǐn)慎!try {// 看看我們能否訪問 c:\test.txtnew FileStream(@"c:\test.txt",FileMode.Open,FileAccess.Read).Close();// 如果我們還在這里,那么很好!accessGranted = true;}catch (...) {}這樣會更加穩(wěn)定,因為無論我們?nèi)绾问?,總會回到最安全的模式?p>9. 模擬方式非常容易受到攻擊
編寫服務(wù)器應(yīng)用程序時,您常常會發(fā)現(xiàn)自己直接或間接使用了 Windows 的一個稱為模擬的很方便的功能。模擬允許進程中的每個線程運行在不同的安全環(huán)境中,通常是客戶端的安全環(huán)境。例如,當(dāng)文件系統(tǒng)重定向器通過網(wǎng)絡(luò)收到一個文件請求時,它對遠(yuǎn)程客戶端進行身份驗證,檢查以確認(rèn)客戶端的請求沒有違反共享上的 DACL,然后把客戶端的標(biāo)記附加到處理請求的線程上,從而模擬客戶端。然后此線程便可以使用客戶端的安全環(huán)境訪問服務(wù)器上的本地文件系統(tǒng)。由于本地文件系統(tǒng)已經(jīng)是安全的,因此這樣做很方便。它會考慮所請求的訪問類型、文件上的 DACL 和線程上的模擬標(biāo)記來進行一個訪問檢查。如果訪問檢查失敗,本地文件系統(tǒng)會將其報告給文件系統(tǒng)重定向器,然后重定向器向遠(yuǎn)程客戶端發(fā)送一個錯誤。毫無疑問,對文件系統(tǒng)重定向器來說這很方便,因為它只是簡單地把請求傳給本地文件系統(tǒng),讓它去做自己的訪問檢查,就好象客戶端在本地一樣。
這對于文件重定向器這樣簡單的網(wǎng)關(guān)而言,一切良好。但模擬常常用在其他更復(fù)雜的應(yīng)用程序中。以一個 Web 應(yīng)用程序為例。如果您編寫一個經(jīng)典的非托管 ASP 程序、ISAPI 擴展或 ASP.NET 應(yīng)用程序,在它的 Web.config 文件中有如下指定
<identity impersonate=‘true‘>那么您的運行環(huán)境將有兩種不同的安全環(huán)境:您將具有一個進程標(biāo)記和一個線程標(biāo)記,一般來說,線程標(biāo)記會被用來做訪問檢查(見圖 3)。假設(shè)您正在編寫一個在 Web 服務(wù)器進程中運行的 ISAPI 應(yīng)用程序,并假定大多數(shù)請求未經(jīng)身份驗證,則您的線程標(biāo)記可能是 IUSR_MACHINE,而進程標(biāo)記卻是 SYSTEM!假設(shè)您的代碼能被一個壞家伙通過緩沖區(qū)溢出利用。您認(rèn)為他會只滿足作為 IUSR_MACHINE 運行嗎?當(dāng)然不會。他的攻擊代碼很可能會調(diào)用 RevertToSelf 以刪除模擬標(biāo)記,從而希望提高他的權(quán)限級別。在這種情況下,他會很容易獲得成功。他還可以調(diào)用 CreateProcess。它不會從模擬標(biāo)記復(fù)制新進程的標(biāo)記,而是從進程標(biāo)記復(fù)制,這樣新進程便可以作為 SYSTEM 運行。
圖 3:檢查 那么怎樣解決這個小問題呢?除了首先確保不出現(xiàn)任何緩沖區(qū)溢出外,還要記住最小授權(quán)原則。如果您的代碼不需要具有 SYSTEM 這樣大的權(quán)限,則不要將 Web 應(yīng)用程序配置為在 Web 服務(wù)器進程中運行。如果只是將 Web 應(yīng)用程序配置為在中等或較高的隔離環(huán)境中運行,您的進程標(biāo)記將會是 IWAM_MACHINE。您實際上沒有任何權(quán)限,因而這種攻擊幾乎不會生效。注意,在 IIS 6.0(即將成為 Windows .NET Server 的一個組件)中,默認(rèn)情況下用戶編寫的代碼不會作為 SYSTEM 運行。基于這樣的認(rèn)識,即開發(fā)人員確實會犯錯誤,Web 服務(wù)器就減少賦予代碼的權(quán)限而提供的任何幫助都是有益的,以免萬一代碼中存在安全問題。 下面是另外一個 COM 程序員可能遇到的隱患。COM 有一個不好的傾向就是敷衍線程。如果您調(diào)用一個進程內(nèi) COM 服務(wù)器,而其線程模型與調(diào)用線程的模型不匹配,則 COM 會在另一個線程上執(zhí)行調(diào)用。COM 不會傳播調(diào)用者線程上的模擬標(biāo)記,這樣結(jié)果就是調(diào)用會在進程的安全環(huán)境中執(zhí)行,而不是在調(diào)用線程的安全環(huán)境中。多么令人吃驚! 下面是另一個由模擬帶來的隱患的情況。假設(shè)您的服務(wù)器接受通過命名管道、DCOM 或 RPC 發(fā)送的請求。您對客戶端進行身份驗證并模擬它們,通過模擬以它們的名義打開內(nèi)核對象。而您又忘了在客戶端斷開連接時關(guān)閉其中的一個對象(例如一個文件)。當(dāng)下一個客戶端進入時,您又對其進行身份驗證和模擬,猜猜會發(fā)生什么?您仍然可以訪問上一個客戶端“遺漏”的文件,即使新的客戶端并沒有獲得訪問該文件的權(quán)限。出于運行性能的原因,內(nèi)核僅在第一次打開對象時對其執(zhí)行訪問檢查。即使您后來因為模擬其他用戶而更改了安全環(huán)境,您還是可以訪問此文件。 以上提及的這些情況都是為了提醒一點,即模擬為服務(wù)器開發(fā)人員提供了方便,但這種方便卻具有很大隱患。在您采用一個模擬標(biāo)記運行程序時,務(wù)必要對自己的代碼多加注意。 10. 編寫非管理員用戶可以實際使用的應(yīng)用程序 這確實是最小授權(quán)原則的必然結(jié)果。如果程序員繼續(xù)開發(fā)這樣的代碼,使得必須是管理員身份的用戶才能在 Windows 上正常運行,我們就不能期望提高系統(tǒng)的安全性。Windows 有一套非常穩(wěn)定的安全功能,但是如果用戶必須具有管理員身份才能進行操作,他們就不能很好地利用這些功能。 您怎樣進行改進呢?首先,自己先嘗試一下,不以管理員身份運行。您很快就會知道使用沒有考慮安全設(shè)計的程序的痛苦。有一天,我 (Keith) 安裝一個由手持設(shè)備制造商提供的軟件,該軟件用于在我的臺式機和手持設(shè)備之間同步數(shù)據(jù)。與往常一樣,我退出了普通的用戶帳戶,然后使用內(nèi)置的管理員帳戶再次登錄,安裝了軟件,然后再次登錄到普通帳戶,并且試圖運行軟件。結(jié)果該應(yīng)用程序跳出一個對話框,說不能訪問某個所需的數(shù)據(jù)文件,接著便給出一個訪問沖突信息。朋友們,這就是某個主流手持設(shè)備廠商的軟件產(chǎn)品。對這種錯誤還有什么借口嗎? 在運行了來自 http://sysinternals.com(英文)的 FILEMON 之后,我很快發(fā)現(xiàn)該應(yīng)用程序試圖打開一個數(shù)據(jù)文件以進行寫入訪問,而該文件與應(yīng)用程序的可執(zhí)行文件安裝在同一目錄中。當(dāng)應(yīng)用程序如預(yù)想的那樣安裝在 Program Files 目錄中時,他們絕不能試圖向該目錄寫入數(shù)據(jù)。Program Files 具有這樣一個限制訪問控制策略是有原因的。我們不希望用戶寫入這些目錄,因為這樣會很容易讓一個用戶留下特洛伊木馬程序,而讓另一個用戶去執(zhí)行。實際上,這個約定是 Windos XP 的基本標(biāo)志性要求之一(請參閱 http://www.microsoft.com/winlogo [英文])。 我們聽到太多的程序員給出借口說他們?yōu)槭裁丛陂_發(fā)代碼時選擇作為管理員身份運行。如果我們繼續(xù)忽略這一問題,只會讓事情更糟。朋友們,編輯一個文本文件并不需要管理員權(quán)限。編輯或調(diào)試一個程序也不需要管理員權(quán)限。在您需要管理員權(quán)限時,請使用操作系統(tǒng)的 RunAs 功能來運行緎com.asp?TARGET=/winlogo/">http://www.microsoft.com/winlogo [英文])。 我們聽到太多的程序員給出借口說他們?yōu)槭裁丛陂_發(fā)代碼時選擇作為管理員身份運行。如果我們繼續(xù)忽略這一問題,只會讓事情更糟。朋友們,編輯一個文本文件并不需要管理員權(quán)限。編輯或調(diào)試一個程序也不需要管理員權(quán)限。在您需要管理員權(quán)限時,請使用操作系統(tǒng)的 RunAs 功能來運行具有較高權(quán)限的單獨的程序。(請參閱 2001 年 11 月的 Security Briefs [英文] 專欄)。如果您是在編寫給開發(fā)人員使用的工具,那么您將對這個群體負(fù)有額外的責(zé)任。我們需要停止這種編寫只有以管理員身份才能運行的代碼的惡性循環(huán),要達(dá)到這一目標(biāo),我們必須從根本上發(fā)生改變。 有關(guān)開發(fā)人員如何能夠輕松地以非管理員身份運行的詳細(xì)信息,請參閱 Keith 的 Web 站點 http://www.develop.com/kbrown(英文)。此外還請看看 Michael 的著作 Writing Secure Code (Microsoft Press, 2001),這本書提供了有關(guān)如何編寫在非管理員環(huán)境下能夠很好運行的應(yīng)用程序的技巧。
有關(guān)相關(guān)文章,請參閱:
Michael Howard 是 Microsoft 公司 Secure Windows Initiative 小組的安全程序經(jīng)理。他還參與編寫了 Writing Secure Code 一書,并編寫了 Design Secure Web-based Applications for Microsoft Windows 2000,這些書都由 Microsoft Press 出版。
Keith Brown 在 DevelopMentor 工作,就程序員對安全問題的意識,進行研究、寫作、教學(xué)和推廣工作。Keith 編寫了 Programming Windows Security (Addison-Wesley, 2000) 一書。他還參與編寫了 Effective COM,現(xiàn)在正在編寫一本有關(guān) .NET 安全性的書。您可以通過 http://www.develop.com/kbrown(英文)與他聯(lián)系。
摘自 MSDN Magazine(英文)2002 年 9 月(英文)號。