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

圖 3:檢查 那么怎樣解決這個(gè)小問(wèn)題呢?除了首先確保不出現任何緩沖區溢出外,還要記住最小授權原則。如果您的代碼不需要具有 SYSTEM 這樣大的權限,則不要將 Web 應用程序配置為在 Web 服務(wù)器進(jìn)程中運行。如果只是將 Web 應用程序配置為在中等或較高的隔離環(huán)境中運行,您的進(jìn)程標記將會(huì )是 IWAM_MACHINE。您實(shí)際上沒(méi)有任何權限,因而這種攻擊幾乎不會(huì )生效。注意,在 IIS 6.0(即將成為 Windows .NET Server 的一個(gè)組件)中,默認情況下用戶(hù)編寫(xiě)的代碼不會(huì )作為 SYSTEM 運行?;谶@樣的認識,即開(kāi)發(fā)人員確實(shí)會(huì )犯錯誤,Web 服務(wù)器就減少賦予代碼的權限而提供的任何幫助都是有益的,以免萬(wàn)一代碼中存在安全問(wèn)題。 下面是另外一個(gè) COM 程序員可能遇到的隱患。COM 有一個(gè)不好的傾向就是敷衍線(xiàn)程。如果您調用一個(gè)進(jìn)程內 COM 服務(wù)器,而其線(xiàn)程模型與調用線(xiàn)程的模型不匹配,則 COM 會(huì )在另一個(gè)線(xiàn)程上執行調用。COM 不會(huì )傳播調用者線(xiàn)程上的模擬標記,這樣結果就是調用會(huì )在進(jìn)程的安全環(huán)境中執行,而不是在調用線(xiàn)程的安全環(huán)境中。多么令人吃驚! 下面是另一個(gè)由模擬帶來(lái)的隱患的情況。假設您的服務(wù)器接受通過(guò)命名管道、DCOM 或 RPC 發(fā)送的請求。您對客戶(hù)端進(jìn)行身份驗證并模擬它們,通過(guò)模擬以它們的名義打開(kāi)內核對象。而您又忘了在客戶(hù)端斷開(kāi)連接時(shí)關(guān)閉其中的一個(gè)對象(例如一個(gè)文件)。當下一個(gè)客戶(hù)端進(jìn)入時(shí),您又對其進(jìn)行身份驗證和模擬,猜猜會(huì )發(fā)生什么?您仍然可以訪(fǎng)問(wèn)上一個(gè)客戶(hù)端“遺漏”的文件,即使新的客戶(hù)端并沒(méi)有獲得訪(fǎng)問(wèn)該文件的權限。出于運行性能的原因,內核僅在第一次打開(kāi)對象時(shí)對其執行訪(fǎng)問(wèn)檢查。即使您后來(lái)因為模擬其他用戶(hù)而更改了安全環(huán)境,您還是可以訪(fǎng)問(wèn)此文件。 以上提及的這些情況都是為了提醒一點(diǎn),即模擬為服務(wù)器開(kāi)發(fā)人員提供了方便,但這種方便卻具有很大隱患。在您采用一個(gè)模擬標記運行程序時(shí),務(wù)必要對自己的代碼多加注意。 10. 編寫(xiě)非管理員用戶(hù)可以實(shí)際使用的應用程序 這確實(shí)是最小授權原則的必然結果。如果程序員繼續開(kāi)發(fā)這樣的代碼,使得必須是管理員身份的用戶(hù)才能在 Windows 上正常運行,我們就不能期望提高系統的安全性。Windows 有一套非常穩定的安全功能,但是如果用戶(hù)必須具有管理員身份才能進(jìn)行操作,他們就不能很好地利用這些功能。 您怎樣進(jìn)行改進(jìn)呢?首先,自己先嘗試一下,不以管理員身份運行。您很快就會(huì )知道使用沒(méi)有考慮安全設計的程序的痛苦。有一天,我 (Keith) 安裝一個(gè)由手持設備制造商提供的軟件,該軟件用于在我的臺式機和手持設備之間同步數據。與往常一樣,我退出了普通的用戶(hù)帳戶(hù),然后使用內置的管理員帳戶(hù)再次登錄,安裝了軟件,然后再次登錄到普通帳戶(hù),并且試圖運行軟件。結果該應用程序跳出一個(gè)對話(huà)框,說(shuō)不能訪(fǎng)問(wèn)某個(gè)所需的數據文件,接著(zhù)便給出一個(gè)訪(fǎng)問(wèn)沖突信息。朋友們,這就是某個(gè)主流手持設備廠(chǎng)商的軟件產(chǎn)品。對這種錯誤還有什么借口嗎? 在運行了來(lái)自 http://sysinternals.com(英文)的 FILEMON 之后,我很快發(fā)現該應用程序試圖打開(kāi)一個(gè)數據文件以進(jìn)行寫(xiě)入訪(fǎng)問(wèn),而該文件與應用程序的可執行文件安裝在同一目錄中。當應用程序如預想的那樣安裝在 Program Files 目錄中時(shí),他們絕不能試圖向該目錄寫(xiě)入數據。Program Files 具有這樣一個(gè)限制訪(fǎng)問(wèn)控制策略是有原因的。我們不希望用戶(hù)寫(xiě)入這些目錄,因為這樣會(huì )很容易讓一個(gè)用戶(hù)留下特洛伊木馬程序,而讓另一個(gè)用戶(hù)去執行。實(shí)際上,這個(gè)約定是 Windos XP 的基本標志性要求之一(請參閱 http://www.microsoft.com/winlogo [英文])。 我們聽(tīng)到太多的程序員給出借口說(shuō)他們?yōu)槭裁丛陂_(kāi)發(fā)代碼時(shí)選擇作為管理員身份運行。如果我們繼續忽略這一問(wèn)題,只會(huì )讓事情更糟。朋友們,編輯一個(gè)文本文件并不需要管理員權限。編輯或調試一個(gè)程序也不需要管理員權限。在您需要管理員權限時(shí),請使用操作系統的 RunAs 功能來(lái)運行緎com.asp?TARGET=/winlogo/">http://www.microsoft.com/winlogo [英文])。 我們聽(tīng)到太多的程序員給出借口說(shuō)他們?yōu)槭裁丛陂_(kāi)發(fā)代碼時(shí)選擇作為管理員身份運行。如果我們繼續忽略這一問(wèn)題,只會(huì )讓事情更糟。朋友們,編輯一個(gè)文本文件并不需要管理員權限。編輯或調試一個(gè)程序也不需要管理員權限。在您需要管理員權限時(shí),請使用操作系統的 RunAs 功能來(lái)運行具有較高權限的單獨的程序。(請參閱 2001 年 11 月的 Security Briefs [英文] 專(zhuān)欄)。如果您是在編寫(xiě)給開(kāi)發(fā)人員使用的工具,那么您將對這個(gè)群體負有額外的責任。我們需要停止這種編寫(xiě)只有以管理員身份才能運行的代碼的惡性循環(huán),要達到這一目標,我們必須從根本上發(fā)生改變。 有關(guān)開(kāi)發(fā)人員如何能夠輕松地以非管理員身份運行的詳細信息,請參閱 Keith 的 Web 站點(diǎn) http://www.develop.com/kbrown(英文)。此外還請看看 Michael 的著(zhù)作 Writing Secure Code (Microsoft Press, 2001),這本書(shū)提供了有關(guān)如何編寫(xiě)在非管理員環(huán)境下能夠很好運行的應用程序的技巧。
有關(guān)相關(guān)文章,請參閱:
Michael Howard 是 Microsoft 公司 Secure Windows Initiative 小組的安全程序經(jīng)理。他還參與編寫(xiě)了 Writing Secure Code 一書(shū),并編寫(xiě)了 Design Secure Web-based Applications for Microsoft Windows 2000,這些書(shū)都由 Microsoft Press 出版。
Keith Brown 在 DevelopMentor 工作,就程序員對安全問(wèn)題的意識,進(jìn)行研究、寫(xiě)作、教學(xué)和推廣工作。Keith 編寫(xiě)了 Programming Windows Security (Addison-Wesley, 2000) 一書(shū)。他還參與編寫(xiě)了 Effective COM,現在正在編寫(xiě)一本有關(guān) .NET 安全性的書(shū)。您可以通過(guò) http://www.develop.com/kbrown(英文)與他聯(lián)系。
摘自 MSDN Magazine(英文)2002 年 9 月(英文)號。
聯(lián)系客服