網絡技術是從1990年代中期發展起來的新技術,它把互聯網上分散的資源融為有機整體,實現資源的全面共享和有機協作,使人們能夠透明地使用資源的整體能力并按需獲取信息。資源包括高性能計算機、存儲資源、數據資源、信息資源、知識資源、專家資源、大型數據庫、網絡、傳感器等。 當前的互聯網只限于信息共享,網絡則被認為是互聯網發展的第三階段。 了解什么是競爭條件,以及它們為什么會引發安全問題。本文向您展示了如何在類 UNIX® (Unix-like)系統中處理常見的競爭條件,包括如何正確地創建鎖文件、鎖文件的替代者,如何處理文件系統,以及如何處理共享目錄(特別是如何在 /tmp 目錄下正確創建臨時目錄)。需要您對信號處理稍有了解。 通過一個偷竊而來的口令,Mallory 成功地登錄到一臺運行 Linux 的重要服務器。其帳號是一個非常受限的帳號,但是 Mallory 知道如何使用它來制造麻煩。Mallory 安裝并運行了一個行為非常奇怪的小程序,該程序使用多個進程在 /tmp 目錄下快速地創建和刪除很多不同的符號鏈接文件。(符號鏈接文件也稱為 symlink,是一種簡單的文件,當被訪問時,它會將請求重定向到另一個文件。)Mallory 的程序不停地創建和刪除很多指向同一特殊文件(/etc/passwd,口令文件)的不同符號鏈接文件。 這臺重要的服務器的安全措施之一是,它每天都運行 Tripwire —— 具體地說,是較老的 2.3.0 版本。Tripwire 是一個檢測重要文件是否被篡改的安全程序。與很多程序一樣,Tripwire 啟動時會嘗試著創建一個臨時文件。Tripwire 會查看并斷定不存在名為“/tmp/twtempa19212”的文件,所以看起來這是一個合適的臨時文件名稱。但是在 Tripwire 完成檢查后,Mallory 的程序就會使用該名稱創建一個符號鏈接文件。這不是偶然的;Mallory 程序的設計目標就是創建最有可能為 Tripwire 所使用的文件名。然后 Tripwire 就會打開該文件,開始寫入臨時信息,但不用創建新的空文件,Tripwire 現在正在重寫口令文件!從那時起,任何人 —— 甚至是管理員 —— 都不能登錄到該系統,因為口令文件已經被破壞了。更糟的是,Mallory 的攻擊完全可以覆蓋所有文件,包括服務器上存儲的重要數據。 競爭條件簡介 這是個假想的故事;“Mallory”是攻擊者的一個慣用名。但是這類攻擊,以及它所利用的缺陷,都極其常見。問題是很多程序都容易受到名為“競爭條件”的安全問題的影響。 當由于事件次序異常而造成對同一資源的競爭,從而導致程序無法正常運行時,就會出現“競爭條件”。注意,競爭條件無需介入同一程序的兩個部分之間的競爭;如果一個外部的攻擊者可以通過意想不到的方式干擾程序,那么就會出現很多安全問題。例如,如果 Tripwire 2.3.0 確定某個文件不存在,它就會嘗試著創建該文件,而不去考慮在進行這兩個步驟期間,該文件是否已經被攻擊者創建。幾十年前,競爭條件還不是什么問題;那時,計算機系統通常在同一時刻只能運行一個單獨的程序,什么都不能打斷它或者與它競爭。但是,當今的計算機通常需要同時運行大量的進程和線程,經常還會有多個處理器確實在同時運行不同的程序。這樣做更靈活,但是有一個危險:如果這些進程和線程共享了所有的資源,那么它們都可能互相影響。實際上,競爭條件缺陷是軟件的更常見缺陷之一,此外,在類 Unix 系統上,/tmp 和 /var/tmp 目錄經常會被錯誤地使用,從而導致競爭條件。 不過,我們首先需要了解一些術語。所有 類-Unix 系統都支持用戶進程;每個進程都有自己的內存空間(其他進程通常無法訪問)。底層的內核會盡量使進程看起來像是在同時運行;在多處理器的系統中,它們確實可以同時運行。從理論上講,一個進程可以擁有一個或多個線程;這些線程可以共享內存。線程也可以同時運行。由于線程可以共享內存,所以,相對于進程,線程之間更有可能產生競爭條件;正是由于這個原因,多線程程序的調試要困難得多。Linux 內核有一個非常好的基本設計:只有線程,并且一些線程可以與其他線程共享內存(這樣實現了傳統的線程),而另外一些線程則不能(這樣就實現了獨立進程)。 為了理解競爭條件,讓我們首先來看一個非常普通的 C 聲明: 清單 1. 普通的 C 聲明 b = b + 1; 看起來非常簡單,不是嗎?但是,讓我們假定有兩個線程在運行這一行代碼,在這里,“b”是一個由兩個線程共享的變量,“b”的初始值為“5”。以下是一個似是而非的執行次序: 清單 2. 使用共享的“b”的可能執行次序 (thread1) load b into some register in thread 1. (thread2) load b into some register in thread 2. (thread1) add 1 to thread 1's register, computing 6. (thread2) add 1 to thread 2's register, computing 6. (thread1) store the register value (6) to b. (thread2) store the register value (6) to b. 初始值為 5,然后兩個線程分別加 1,但是最終的結果是 6... 而不是應該得到的 7。問題在于,兩個線程互相干擾,從而導致產生錯誤的最終答案。 通常,線程不是以原子的方式執行的;另一個線程可以在任何兩個指令期間打斷它,而且還可以使用一些共享的資源。如果一個安全程序的線程沒有預防這些中斷,那么另一個線程就可以干擾該安全程序的線程。在安全程序中,不管在任何一對指令中間運行了多少其他線程的代碼,程序都必須正確地運行。關鍵是,當您的程序訪問任意資源時,要確定其他某個線程是否可能因為使用該資源對您的程序造成干擾。 解決競爭條件 競爭條件的典型解決方案是,確保程序在使用某個資源(比如文件、設備、對象或者變量)時,擁有自己的專有權。獲得某個資源的專有權的過程稱為加鎖。鎖不太容易處理。死鎖(“抱死,deadly embrace”)是常見的問題,在這種情形下,程序會因等待對方釋放被加鎖的資源而無法繼續運行。要求所有線程都必須按照相同的順序(比如,按字母排序,或者從“largest grain”到“smallest grain”的順序)獲得鎖,這樣可以避免大部分死鎖。另一個常見問題是活鎖(livelock),在這種情況下,程序至少成功地獲得和釋放了一個鎖,但是以這種方式無法將程序再繼續運行下去。如果一個鎖被掛起,順利地釋放它會很難。簡言之,編譯在任何情況下都可以按需要正確地加鎖和釋放的程序通常很困難。 有時,可以一次執行一個單獨操作來完成一些特殊的操作,從而使您不需要顯式地對某個資源進行加鎖而后再解鎖。這類操作稱為“原子”操作,只要能夠使用這類操作,它們通常是最好的解決方案。 有一些錯誤是如此常見,所以,為了避免犯這些錯誤,您需要了解它們。一個問題是,以不總是鎖定某資源的方式創建鎖文件;您應該學習如何正確創建它們,或者轉而采取不同的加鎖機制。您還需要正確地處理文件系統中的競爭,其中包括如何處理永遠危險的共享目錄 /tmp 和 /var/tmp,以及如何安全地使用信號。下一章中將描述如何安全使用它們。 網絡的神奇作用吸引著越來越多的用戶加入其中,正因如此,網絡的承受能力也面臨著越來越嚴峻的考驗―從硬件上、軟件上、所用標準上......,各項技術都需要適時應勢,對應發展,這正是網絡迅速走向進步的催化劑。 |
溫馨提示:喜歡本站的話,請收藏一下本站!