程序員要注意的十大安全技巧

2010-08-28 10:44:58來源:西部e網(wǎng)作者:

  安全問題涉及許多方面。安全風(fēng)險(xiǎn)可能來自任何地方。您可能編寫了無效的錯(cuò)誤處理代碼,或者在賦予權(quán)限時(shí)過于慷慨。您可能忘記了在您的服務(wù)器上正在運(yùn)行什么服務(wù)。您可能接受了所有用戶輸入。如此等等。為使您在保護(hù)自己的計(jì)算機(jī)、網(wǎng)絡(luò)和代碼方面有個(gè)良好開端,這里展示了十條技巧,遵循這些技巧可以獲得一個(gè)更安全的網(wǎng)絡(luò)策略。

  1. 信任用戶的輸入會將自己置于險(xiǎn)境

  即使不閱讀余下的內(nèi)容,也要記住一點(diǎn),“不要信任用戶輸入”。如果您總是假設(shè)數(shù)據(jù)是有效的并且沒有惡意,那么問題就來了。大多數(shù)安全薄弱環(huán)節(jié)都與攻擊者向服務(wù)器提供惡意編寫的數(shù)據(jù)有關(guān)。

  信任輸入的正確性可能會導(dǎo)致緩沖區(qū)溢出、跨站點(diǎn)腳本攻擊、SQL 插入代碼攻擊等等。

  讓我們詳細(xì)討論一下這些潛在攻擊方式。

  2. 防止緩沖區(qū)溢出

  當(dāng)攻擊者提供的數(shù)據(jù)長度大于應(yīng)用程序的預(yù)期時(shí),便會發(fā)生緩沖區(qū)溢出,此時(shí)數(shù)據(jù)會溢出到內(nèi)部存儲器空間。緩沖區(qū)溢出主要是一個(gè) C/C++ 問題。它們是種威脅,但通常很容易修補(bǔ)。我們只看到過兩個(gè)不明顯且難以修復(fù)的緩沖區(qū)溢出。開發(fā)人員沒有預(yù)料到外部提供的數(shù)據(jù)會比內(nèi)部緩沖區(qū)大。溢出導(dǎo)致了內(nèi)存中其他數(shù)據(jù)結(jié)構(gòu)的破壞,這種破壞通常會被攻擊者利用,以運(yùn)行惡意代碼。數(shù)組索引錯(cuò)誤也會造成緩沖區(qū)下溢和超限,但這種情況沒那么普遍。

  請看以下 C++ 代碼片段:
void DoSomething(char *cBuffSrc, DWORD cbBuffSrc) {
char cBuffDest[32];
memcpy(cBuffDest,cBuffSrc,cbBuffSrc);
}

  問題在哪里?事實(shí)上,如果 cBuffSrc 和 cbBuffSrc 來自可信賴的源(例如不信任數(shù)據(jù)并因此而驗(yàn)證數(shù)據(jù)的有效性和大小的代碼),則這段代碼沒有任何問題。然而,如果數(shù)據(jù)來自不可信賴的源,也未得到驗(yàn)證,那么攻擊者(不可信賴源)很容易就可以使 cBuffSrc 比 cBuffDest 大,同時(shí)也將 cbBuffSrc 設(shè)定為比 cBuffDest 大。當(dāng) memcpy 將數(shù)據(jù)復(fù)制到 cBuffDest 中時(shí),來自 DoSomething 的返回地址就會被更改,因?yàn)?cBuffDest 在函數(shù)的堆棧框架上與返回地址相鄰,此時(shí)攻擊者即可通過代碼執(zhí)行一些惡意操作。

  彌補(bǔ)的方法就是不要信任用戶的輸入,并且不信任 cBuffSrc 和 cbBuffSrc 中攜帶的任何數(shù)據(jù):

void DoSomething(char *cBuffSrc, DWORD cbBuffSrc) {
const DWORD cbBuffDest = 32;
char cBuffDest[cbBuffDest];
#ifdef _DEBUG
memset(cBuffDest, 0x33, cbBuffSrc);
#endif
memcpy(cBuffDest, cBuffSrc, min(cbBuffDest, cbBuffSrc));
}

  此函數(shù)展示了一個(gè)能夠減少緩沖區(qū)溢出的正確編寫的函數(shù)的三個(gè)特性。首先,它要求調(diào)用者提供緩沖區(qū)的長度。當(dāng)然,您不能盲目相信這個(gè)值!接下來,在一個(gè)調(diào)試版本中,代碼將探測緩沖區(qū)是否真的足夠大,以便能夠存放源緩沖區(qū)。如果不能,則可能觸發(fā)一個(gè)訪問沖突并把代碼載入調(diào)試器。在調(diào)試時(shí),您會驚奇地發(fā)現(xiàn)竟有如此多的錯(cuò)誤。最后也是最重要的是,對 memcpy 的調(diào)用是防御性的,它不會復(fù)制多于目標(biāo)緩沖區(qū)存放能力的數(shù)據(jù)。

  在 Windows? Security Push at Microsoft(Microsoft Windows? 安全推動(dòng)活動(dòng))中,我們?yōu)?C 程序員創(chuàng)建了一個(gè)安全字符串處理函數(shù)列表。您可以在 Strsafe.h: Safer String Handling in C(英文)中找到它們。

 3. 防止跨站點(diǎn)腳本

  跨站點(diǎn)腳本攻擊是 Web 特有的問題,它能通過單個(gè) Web 頁中的一點(diǎn)隱患危害客戶端的數(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ù)始終是有效的,并且只是包含了一個(gè)名稱。但攻擊者會濫用這段代碼,將腳本和 HTML 代碼作為名稱提供。如果輸入如下的 URL http://northwindtraders.com/welcome.aspx?name=<script>alert('您好!'); </script>

  您將得到一個(gè)網(wǎng)頁,上面顯示一個(gè)對話框,顯示“您好!”。您可能會說,“那又怎樣?”想像一下,攻擊者可以誘導(dǎo)用戶點(diǎn)擊這樣的鏈接,但查詢字符串中卻包含一些真正危險(xiǎn)的腳本和 HTML,由此會得到用戶的 cookie 并把它發(fā)送到攻擊者擁有的網(wǎng)站;現(xiàn)在攻擊者便獲得了您的私人 cookie 信息,或許會更糟。

  要避免這種情況,有兩種方法。第一種是不信任輸入,并嚴(yán)格限制用戶名所包含的內(nèi)容。例如,可以使用正則表達(dá)式檢查該名稱是否只包含一個(gè)普通的字符子集,并且不太大。以下 C# 代碼片段顯示了完成這一步驟的方法:

Regex r = new Regex(@"^[w]{1,40}$");

if (r.Match(strName).Success) {
// 好!字符串沒問題
} else {
// 不好!字符串無效
}

  這段代碼使用正則表達(dá)式驗(yàn)證一個(gè)字符串僅包含 1 到 40 個(gè)字母或數(shù)字。這是確定一個(gè)值是否正確的唯一安全方法。

  HTML 或腳本不可能蒙混過此正則表達(dá)式!不要使用正則表達(dá)式尋找無效字符并在發(fā)現(xiàn)這種無效字符后拒絕請求,因?yàn)槿菀壮霈F(xiàn)漏掉的情況。 第二種防范措施是對所有作為輸出的輸入進(jìn)行 HTML 編碼。這會減少危險(xiǎn)的 HTML 標(biāo)記,使之變成更安全的轉(zhuǎn)義符。您可以在 ASP.NET 中使用 HttpServerUtility.HtmlEncode,或者在 ASP 中使用 Server.HTMLEncode 轉(zhuǎn)義任何可能出現(xiàn)問題的字符串。

  4. 不要請求 sa 權(quán)限  

  我們要討論的最后一種輸入信任攻擊是 SQL 插入代碼。許多開發(fā)人員編寫這樣的代碼,即獲取輸入并使用該輸入來建立 SQL 查詢,進(jìn)而與后臺數(shù)據(jù)存儲(如 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è)嚴(yán)重缺陷。首先,它是以系統(tǒng)管理員帳戶 sa 建立從 Web 服務(wù)到 SQL Server 的連接的。不久您就會看到這樣做的缺陷所在。第二點(diǎn),注意使用“password”作為 sa 帳戶密碼的聰明做法!
但真正值得關(guān)注的是構(gòu)造 SQL 語句的字符串連接。如果用戶為 ID 輸入 1001,您會得到如下 SQL 語句,它是完全有效的。

  SELECT hasshipped FROM shipping WHERE id = '1001'

  但攻擊者比這要有創(chuàng)意得多。他們會為 ID 輸入一個(gè)“'1001' DROP table shipping --”,它將執(zhí)行如下查詢:

SELECT hasshipped FROM
shipping WHERE id = '1001'
DROP table shipping -- ';

  它更改了查詢的工作方式。這段代碼不僅會嘗試判斷是否裝運(yùn)了某些貨物,它還會繼續(xù) drop(刪除)shipping 表!操作符 -- 是 SQL 中的注釋操作符,它使攻擊者能夠更容易地構(gòu)造一系列有效但危險(xiǎn)的 SQL 語句!

  這時(shí)您也許會覺得奇怪,怎么任何一個(gè)用戶都能刪除 SQL Server 數(shù)據(jù)庫中的表呢。當(dāng)然,您是對的,只有管理員才能做這樣的工作。但這里您是作為 sa 連接到數(shù)據(jù)庫的,而 sa 能在 SQL Server 數(shù)據(jù)庫上做他想做的任何事。永遠(yuǎn)不要在任何應(yīng)用程序中以 sa 連接 SQL Server;正確的做法是,如果合適,使用 Windows 集成的身份驗(yàn)證,或者以一個(gè)預(yù)先定義的具有適當(dāng)權(quán)限的帳戶連接。

  修復(fù) SQL 插入代碼問題很容易。使用 SQL 存儲過程及參數(shù),下面的代碼展示了創(chuàng)建這種查詢的方法 - 以及如何使用正則表達(dá)式來確認(rèn)輸入有效,因?yàn)槲覀兊慕灰滓?guī)定貨運(yùn) 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ū)溢出、跨站點(diǎn)腳本和 SQL 插入代碼攻擊都是信任輸入問題的示例。所有這些攻擊都能通過一種機(jī)制來減輕危害,即認(rèn)為所有輸入都是有害的,除非獲得證明。

  5. 注意加密代碼!

  下面我們來看些會讓我們吃驚的東西。我發(fā)現(xiàn)我們檢查的安全代碼中百分之三十以上都存在安全漏洞。最常見的漏洞可能就是自己的加密代碼,這些代碼很可能不堪一擊。永遠(yuǎn)不要?jiǎng)?chuàng)建自己的加密代碼,那是徒勞的。不要認(rèn)為僅僅因?yàn)槟凶约旱募用芩惴ㄆ渌司蜔o法破解。攻擊者能使用調(diào)試器,他們也有時(shí)間和知識來確認(rèn)系統(tǒng)如何工作 - 通常在幾小時(shí)內(nèi)就會破解它們。您應(yīng)該使用 Win32? 的 CryptoAPI,System.Security.Cryptography 命名空間提供了大量優(yōu)秀且經(jīng)過測試的加密算法。

  6. 減少自己被攻擊的可能性

  如果沒有百分之九十以上的用戶要求,則不應(yīng)默認(rèn)安裝某一功能。Internet">Internet">Internet">Internet Information Services (IIS) 6.0 遵循了這一安裝建議,您可以在這個(gè)月發(fā)布的 Wayne Berry 的文章“Innovations in Internet">Internet">Internet">Internet Information Services Let You Tightly Guard Secure Data and Server Processes”中讀到相關(guān)內(nèi)容。這種安裝策略背后的思想是您不會注意自己并未使用的服務(wù),如果這些服務(wù)正在運(yùn)行,則可能被其他人利用。如果默認(rèn)安裝某功能,則它應(yīng)在最小授權(quán)原則下運(yùn)行。也就是說,除非必要,否則不要允許使用管理員權(quán)限運(yùn)行應(yīng)用程序。最好遵循這一忠告。

  7. 使用最小授權(quán)原則

  出于若干原因,操作系統(tǒng)和公共語言運(yùn)行時(shí)有一個(gè)安全策略。很多人以為此安全策略存在的主要原因是防止用戶有意破壞:訪問他們無權(quán)訪問的文件、重新配置網(wǎng)絡(luò)以達(dá)到他們的要求以及其他惡劣行為。的確,這種來自內(nèi)部的攻擊很普遍,也需要防范,但還有另一個(gè)原因需要嚴(yán)守這一安全策略。即在代碼周圍建立起防范壁壘以防止用戶有意或(正如經(jīng)常發(fā)生的)無意的操作對網(wǎng)絡(luò)造成嚴(yán)重破壞。例如,通過電子郵件下載的附件在 Alice 的機(jī)器上執(zhí)行時(shí)被限制為只能訪問 Alice 可以訪問的資源。如果附件中含有特洛伊木馬,那么好的安全策略就是限制它所能產(chǎn)生的破壞。 當(dāng)您設(shè)計(jì)、建立并部署服務(wù)器應(yīng)用程序時(shí),您不能假設(shè)所有請求都來自合法用戶。如果一個(gè)壞家伙發(fā)送給您一個(gè)惡意請求(但愿不會如此)并使您的代碼產(chǎn)生惡劣操作,您會希望您的應(yīng)用程序擁有所有可能的防護(hù)來限制損害。因此我們認(rèn)為,您的公司實(shí)施安全策略不僅是因?yàn)樗恍湃文蚰拇a,同時(shí)也是為了保護(hù)不受外界有企圖的代碼的傷害。

  最小授權(quán)原則認(rèn)為,要在最少的時(shí)間內(nèi)授予代碼所需的最低權(quán)限。也就是說,任何時(shí)候都應(yīng)在您的代碼周圍豎起盡可能多的防護(hù)墻。當(dāng)發(fā)生某些不好的事情時(shí) - 就象 Murphy 定律保證的那樣 - 您會很高興這些防護(hù)墻都處在合適的位置上。因此,這里就使用最小授權(quán)原則運(yùn)行代碼給出了一些具體方法。

  為您的服務(wù)器代碼選擇一個(gè)安全環(huán)境,僅允許其訪問完成其工作所必需的資源。如果您代碼中的某些部分要求很高的權(quán)限,請考慮將這部分代碼分離出來并單獨(dú)以較高的權(quán)限運(yùn)行。為安全分離這一以不同的操作系統(tǒng)驗(yàn)證信息運(yùn)行的代碼,您最好在一個(gè)單獨(dú)的進(jìn)程(運(yùn)行在具有更高權(quán)限的安全環(huán)境中)中運(yùn)行此代碼。這意味著您將需要進(jìn)程間通訊(如 COM 或 Microsoft .NET 遠(yuǎn)程處理),并且需要設(shè)計(jì)該代碼的接口以使往返行程最小。

  如果在 .NET Framework 環(huán)境中將代碼分離成程序集,請考慮每段代碼所需的權(quán)限級別。您會發(fā)現(xiàn)這是一個(gè)很容易的過程:把需要較高權(quán)限的代碼分離到可賦予其更多權(quán)限的單獨(dú)的程序集中,同時(shí)使其余大部分程序集以較低的權(quán)限運(yùn)行,從而在您的代碼周圍添加更多的防護(hù)。 在進(jìn)行此操作時(shí),不要忘了,由于代碼訪問安全 (CAS) 堆棧的作用,您限制的不僅是自己程序集的權(quán)限,也包括您調(diào)用的任何程序集的權(quán)限。

  許多人建立了自己的應(yīng)用程序,使得其產(chǎn)品在測試并提供給客戶后可以插入新的組件。保護(hù)這種類型的應(yīng)用程序非常困難,因?yàn)槟鸁o法測試所有可能的代碼路徑來發(fā)現(xiàn)錯(cuò)誤和安全漏洞。然而,如果您的應(yīng)用程序是托管的,則 CLR 提供了一個(gè)極好的功能,可以使用它關(guān)閉這些可擴(kuò)展點(diǎn)。通過聲明一個(gè)權(quán)限對象或一個(gè)權(quán)限集并調(diào)用 PermitOnly 或 Deny,您可以為自己的堆棧添加一個(gè)標(biāo)記,它將阻塞授予您調(diào)用的任何代碼的權(quán)限。通過在調(diào)用某個(gè)插件之前進(jìn)行此操作,您就可以限制該插件所能執(zhí)行的任務(wù)。例如,一個(gè)用于計(jì)算分期付款的插件不需要任何訪問文件系統(tǒng)的權(quán)限。這只是最小權(quán)限的另一個(gè)例子,由此您可以事先保護(hù)自己。請確保記錄下這些限制,并注意,具有較高權(quán)限的插件能夠使用 Assert 語句逃避這些限制。

  8. 注意失敗模式

  接受它吧。其他人和您一樣憎恨編寫錯(cuò)誤處理代碼。導(dǎo)致代碼失敗的原因如此眾多,一想到這些就讓人沮喪。大多數(shù)程序員,包括我們,更愿意關(guān)注正常的執(zhí)行路徑。那里才是真正完成工作的地方。讓我們盡可能快而無痛地完成這些錯(cuò)誤處理,然后繼續(xù)下一行真正的代碼吧。

  只可惜,這種情緒并不安全。相反,我們需要更密切地關(guān)注代碼中的失敗模式。人們對這些代碼的編寫通常很少深入注意,并且常常沒有經(jīng)過完全測試。還記得最后一次您完全肯定調(diào)試過函數(shù)的每一行代碼,包括其中每一個(gè)很小的錯(cuò)誤處理程序是什么時(shí)候?

  未經(jīng)測試的代碼常會導(dǎo)致安全漏洞。有三件事情可以幫助您減輕這個(gè)問題。首先,對那些很小的錯(cuò)誤處理程序給予和正常代碼同樣的關(guān)注。考慮當(dāng)您的錯(cuò)誤處理代碼執(zhí)行時(shí)系統(tǒng)的狀態(tài)。系統(tǒng)是否處于有效并且安全的狀態(tài)中?其次,一旦您編寫了一個(gè)函數(shù),請逐步將它徹底調(diào)試幾遍,確保測試每一個(gè)錯(cuò)誤處理程序。注意,即使使用這樣的技術(shù),也可能無法發(fā)現(xiàn)非常隱秘的計(jì)時(shí)錯(cuò)誤。您可能需要給您的函數(shù)傳遞錯(cuò)誤參數(shù),或者以某種方式調(diào)整系統(tǒng)的狀態(tài),以使您的錯(cuò)誤處理程序得以執(zhí)行。通過花時(shí)間單步調(diào)試代碼,您可以慢下來并有足夠的時(shí)間來查看代碼以及系統(tǒng)運(yùn)行時(shí)的狀態(tài)。通過在調(diào)試器中仔細(xì)單步執(zhí)行代碼,我們在自己的編程邏輯中發(fā)現(xiàn)了許多缺陷。這是一個(gè)已得到證明的技術(shù)。請使用這一技術(shù)。最后,確保您的測試組合能使您的函數(shù)進(jìn)行失敗測試。盡量使測試組合能夠檢驗(yàn)函數(shù)中的每一行代碼。這能幫助您發(fā)現(xiàn)規(guī)律,特別是當(dāng)使測試自動(dòng)化并在每次建立代碼后運(yùn)行測試時(shí)。 關(guān)于失敗模式還有一件非常重要的事情需要說明。當(dāng)您的代碼失敗時(shí)要確保系統(tǒng)處于可能的最安全狀態(tài)。下面顯示了一些有問題的代碼:


bool accessGranted = true; // 過于樂觀!
try {
// 看看我們能否訪問 c:test.txt
new FileStream(@"c:test.txt",
FileMode.Open,
FileAccess.Read).Close();
}
catch (SecurityException x) {
// 訪問被拒絕
accessGranted = false;
}
catch (...) {
// 發(fā)生了其他事情
}

  盡管我們使用了 CLR,我們?nèi)员辉试S訪問該文件。在這種情況下,并沒有引發(fā)一個(gè) SecurityException。但是,例如,如果文件的自由訪問控制列表 (DACL) 不允許我們訪問呢?這時(shí),會引發(fā)另一種類型的異常。但由于代碼第一行的樂觀假設(shè),我們永遠(yuǎn)也不會知道這一點(diǎn)。

  編寫這段代碼的一種更好的方法就是持謹(jǐn)慎態(tài)度:

bool accessGranted = false; // 保持謹(jǐn)慎!
try {
// 看看我們能否訪問 c:test.txt
new FileStream(@"c:test.txt",
FileMode.Open,
FileAccess.Read).Close();
// 如果我們還在這里,那么很好!
accessGranted = true;
}
catch (...) {}

  這樣會更加穩(wěn)定,因?yàn)闊o論我們?nèi)绾问。倳氐阶畎踩哪J健?
  
  9. 模擬方式非常容易受到攻擊

  編寫服務(wù)器應(yīng)用程序時(shí),您常常會發(fā)現(xiàn)自己直接或間接使用了 Windows 的一個(gè)稱為模擬的很方便的功能。模擬允許進(jìn)程中的每個(gè)線程運(yùn)行在不同的安全環(huán)境中,通常是客戶端的安全環(huán)境。例如,當(dāng)文件系統(tǒng)重定向器通過網(wǎng)絡(luò)收到一個(gè)文件請求時(shí),它對遠(yuǎn)程客戶端進(jìn)行身份驗(yàn)證,檢查以確認(rèn)客戶端的請求沒有違反共享上的 DACL,然后把客戶端的標(biāo)記附加到處理請求的線程上,從而模擬客戶端。然后此線程便可以使用客戶端的安全環(huán)境訪問服務(wù)器上的本地文件系統(tǒng)。由于本地文件系統(tǒng)已經(jīng)是安全的,因此這樣做很方便。它會考慮所請求的訪問類型、文件上的 DACL 和線程上的模擬標(biāo)記來進(jìn)行一個(gè)訪問檢查。如果訪問檢查失敗,本地文件系統(tǒng)會將其報(bào)告給文件系統(tǒng)重定向器,然后重定向器向遠(yuǎn)程客戶端發(fā)送一個(gè)錯(cuò)誤。毫無疑問,對文件系統(tǒng)重定向器來說這很方便,因?yàn)樗皇呛唵蔚匕颜埱髠鹘o本地文件系統(tǒng),讓它去做自己的訪問檢查,就好象客戶端在本地一樣。 這對于文件重定向器這樣簡單的網(wǎng)關(guān)而言,一切良好。但模擬常常用在其他更復(fù)雜的應(yīng)用程序中。以一個(gè) Web 應(yīng)用程序?yàn)槔H绻帉懸粋(gè)經(jīng)典的非托管 ASP 程序、ISAPI 擴(kuò)展或 ASP.NET 應(yīng)用程序,在它的 Web.config 文件中有如下指定

 。糹dentity impersonate='true'>

  那么您的運(yùn)行環(huán)境將有兩種不同的安全環(huán)境:您將具有一個(gè)進(jìn)程標(biāo)記和一個(gè)線程標(biāo)記,一般來說,線程標(biāo)記會被用來做訪問檢查(見圖 )。假設(shè)您正在編寫一個(gè)在 Web 服務(wù)器進(jìn)程中運(yùn)行的 ISAPI 應(yīng)用程序,并假定大多數(shù)請求未經(jīng)身份驗(yàn)證,則您的線程標(biāo)記可能是 IUSR_MACHINE,而進(jìn)程標(biāo)記卻是 SYSTEM!假設(shè)您的代碼能被一個(gè)壞家伙通過緩沖區(qū)溢出利用。您認(rèn)為他會只滿足作為 IUSR_MACHINE 運(yùn)行嗎?當(dāng)然不會。他的攻擊代碼很可能會調(diào)用 RevertToSelf 以刪除模擬標(biāo)記,從而希望提高他的權(quán)限級別。在這種情況下,他會很容易獲得成功。他還可以調(diào)用 CreateProcess。它不會從模擬標(biāo)記復(fù)制新進(jìn)程的標(biāo)記,而是從進(jìn)程標(biāo)記復(fù)制,這樣新進(jìn)程便可以作為 SYSTEM 運(yùn)行。

  那么怎樣解決這個(gè)小問題呢?除了首先確保不出現(xiàn)任何緩沖區(qū)溢出外,還要記住最小授權(quán)原則。如果您的代碼不需要具有 SYSTEM 這樣大的權(quán)限,則不要將 Web 應(yīng)用程序配置為在 Web 服務(wù)器進(jìn)程中運(yùn)行。如果只是將 Web 應(yīng)用程序配置為在中等或較高的隔離環(huán)境中運(yùn)行,您的進(jìn)程標(biāo)記將會是 IWAM_MACHINE。您實(shí)際上沒有任何權(quán)限,因而這種攻擊幾乎不會生效。注意,在 IIS 6.0(即將成為 Windows .NET Server 的一個(gè)組件)中,默認(rèn)情況下用戶編寫的代碼不會作為 SYSTEM 運(yùn)行;谶@樣的認(rèn)識,即開發(fā)人員確實(shí)會犯錯(cuò)誤,Web 服務(wù)器就減少賦予代碼的權(quán)限而提供的任何幫助都是有益的,以免萬一代碼中存在安全問題。

  下面是另外一個(gè) COM 程序員可能遇到的隱患。COM 有一個(gè)不好的傾向就是敷衍線程。如果您調(diào)用一個(gè)進(jìn)程內(nèi) COM 服務(wù)器,而其線程模型與調(diào)用線程的模型不匹配,則 COM 會在另一個(gè)線程上執(zhí)行調(diào)用。COM 不會傳播調(diào)用者線程上的模擬標(biāo)記,這樣結(jié)果就是調(diào)用會在進(jìn)程的安全環(huán)境中執(zhí)行,而不是在調(diào)用線程的安全環(huán)境中。多么令人吃驚!

  下面是另一個(gè)由模擬帶來的隱患的情況。假設(shè)您的服務(wù)器接受通過命名管道、DCOM 或 RPC 發(fā)送的請求。您對客戶端進(jìn)行身份驗(yàn)證并模擬它們,通過模擬以它們的名義打開內(nèi)核對象。而您又忘了在客戶端斷開連接時(shí)關(guān)閉其中的一個(gè)對象(例如一個(gè)文件)。當(dāng)下一個(gè)客戶端進(jìn)入時(shí),您又對其進(jìn)行身份驗(yàn)證和模擬,猜猜會發(fā)生什么?您仍然可以訪問上一個(gè)客戶端“遺漏”的文件,即使新的客戶端并沒有獲得訪問該文件的權(quán)限。出于運(yùn)行性能的原因,內(nèi)核僅在第一次打開對象時(shí)對其執(zhí)行訪問檢查。即使您后來因?yàn)槟M其他用戶而更改了安全環(huán)境,您還是可以訪問此文件。 以上提及的這些情況都是為了提醒一點(diǎn),即模擬為服務(wù)器開發(fā)人員提供了方便,但這種方便卻具有很大隱患。在您采用一個(gè)模擬標(biāo)記運(yùn)行程序時(shí),務(wù)必要對自己的代碼多加注意。

  10. 編寫非管理員用戶可以實(shí)際使用的應(yīng)用程序

  這確實(shí)是最小授權(quán)原則的必然結(jié)果。如果程序員繼續(xù)開發(fā)這樣的代碼,使得必須是管理員身份的用戶才能在 Windows 上正常運(yùn)行,我們就不能期望提高系統(tǒng)的安全性。Windows 有一套非常穩(wěn)定的安全功能,但是如果用戶必須具有管理員身份才能進(jìn)行操作,他們就不能很好地利用這些功能。

  您怎樣進(jìn)行改進(jìn)呢?首先,自己先嘗試一下,不以管理員身份運(yùn)行。您很快就會知道使用沒有考慮安全設(shè)計(jì)的程序的痛苦。有一天,我安裝一個(gè)由手持設(shè)備制造商提供的軟件,該軟件用于在我的臺式機(jī)和手持設(shè)備之間同步數(shù)據(jù)。與往常一樣,我退出了普通的用戶帳戶,然后使用內(nèi)置的管理員帳戶再次登錄,安裝了軟件,然后再次登錄到普通帳戶,并且試圖運(yùn)行軟件。結(jié)果該應(yīng)用程序跳出一個(gè)對話框,說不能訪問某個(gè)所需的數(shù)據(jù)文件,接著便給出一個(gè)訪問沖突信息。朋友們,這就是某個(gè)主流手持設(shè)備廠商的軟件產(chǎn)品。對這種錯(cuò)誤還有什么借口嗎?

  在運(yùn)行了來自 http://sysinternals.com(英文)的 FILEMON 之后,我很快發(fā)現(xiàn)該應(yīng)用程序試圖打開一個(gè)數(shù)據(jù)文件以進(jìn)行寫入訪問,而該文件與應(yīng)用程序的可執(zhí)行文件安裝在同一目錄中。當(dāng)應(yīng)用程序如預(yù)想的那樣安裝在 Program Files 目錄中時(shí),他們絕不能試圖向該目錄寫入數(shù)據(jù)。Program Files 具有這樣一個(gè)限制訪問控制策略是有原因的。我們不希望用戶寫入這些目錄,因?yàn)檫@樣會很容易讓一個(gè)用戶留下特洛伊木馬程序,而讓另一個(gè)用戶去執(zhí)行。實(shí)際上,這個(gè)約定是 Windos XP 的基本標(biāo)志性要求之一。

  我們聽到太多的程序員給出借口說他們?yōu)槭裁丛陂_發(fā)代碼時(shí)選擇作為管理員身份運(yùn)行。如果我們繼續(xù)忽略這一問題,只會讓事情更糟。朋友們,編輯一個(gè)文本文件并不需要管理員權(quán)限。編輯或調(diào)試一個(gè)程序也不需要管理員權(quán)限。在您需要管理員權(quán)限時(shí),請使用操作系統(tǒng)的 RunAs 功能來運(yùn)行。

  我們聽到太多的程序員給出借口說他們?yōu)槭裁丛陂_發(fā)代碼時(shí)選擇作為管理員身份運(yùn)行。如果我們繼續(xù)忽略這一問題,只會讓事情更糟。朋友們,編輯一個(gè)文本文件并不需要管理員權(quán)限。編輯或調(diào)試一個(gè)程序也不需要管理員權(quán)限。在您需要管理員權(quán)限時(shí),請使用操作系統(tǒng)的 RunAs 功能來運(yùn)行具有較高權(quán)限的單獨(dú)的程序。如果您是在編寫給開發(fā)人員使用的工具,那么您將對這個(gè)群體負(fù)有額外的責(zé)任。我們需要停止這種編寫只有以管理員身份才能運(yùn)行的代碼的惡性循環(huán),要達(dá)到這一目標(biāo),我們必須從根本上發(fā)生改變。

關(guān)鍵詞:程序員

贊助商鏈接: