軟件安全


軟件安全問題


  1. Web應用安全性漏洞中,其中包括未經驗證的輸入、跨站點腳本、緩衝區溢出、注入攻擊( injection flaws )和不恰當的錯誤處理。程序中的錯誤代碼以及沒有充分進行檢查和驗證的數據造成了這些缺陷的發生。
  2. 軟件安全與軟件的質量和可靠性緊密相關,但又略有不同。軟件的質量和可靠性關心的是一個程序是否意外出錯,這些錯誤是由一些隨機的未預料到的輸入、系統交互或者使用錯誤代碼引起的,它們服從一些形式的概率分佈。提高軟件質量通常的方法,是採用某些形式的結構化設計,井通過測試來識別和消除程序中儘量多的bug ,一般包括可能的輸入變化和常見的錯誤測試。
  3. 軟件安全不同於軟件的質量和可靠性,攻擊者不會依據一定的概率分佈實施攻擊,他們的目標是那些特殊的可以利用的bug ,從而造成程序失敗。
  4. 防禦性程序設計(defensive programming ):是防禦性設計的一種形式,目的是保證軟件的連續性功能。
  5. 防禦性程序設計有時又被稱爲安全程序設計( secure programming )。這是因爲攻擊者可能會利用軟件的bug ,實現代碼注入攻擊、拒絕服務攻擊或者其他形式的攻擊。防禦性程序設計和正常的編程之間的一個不同是不需要任何假設,所有的錯誤狀態都要進行說明和處理。簡短地說,程序員在代碼裏從來不假設一個特定的函數調用或者庫函數會像它說明的那樣工作,從而不做任何檢查就進行處理。
  6. 對一個程序將要接收的輸入類型和程序執行的環境做出各種各樣的假設。防禦性程序設計意味着程序需要驗證所有這些假設,所有可能的失敗都能安全地、完美地得到解決。
  7. 改編一個程序時,防禦性程序設計又意味着程序員必須仔細檢查他所做的每一個假設,檢查和處理所有可能的錯誤,仔細檢查與存在的代碼之間的每一個交互。不能識別和處理這些交互可能會導致錯誤的程序行爲,以及將一個原來安全的程序變成一個存在漏洞的程序。

處理程序輸入


  1. 程序輸入是指程序之外的任意數據源,程序員在編寫代碼的時候並不清楚地知道這些數據的值。程序輸入顯然包括了從用戶鍵盤、鼠標、文件或者網絡連接讀入到程序中的數據。然而,它也包含了在執行環境中提供給程序的數據,程序從文件讀入的任意配置值或者其他的數據,以及操作系統提供給程序的值
  2. 任何程序輸入都有兩個關鍵點需要我們考慮,這就是輸入的長度以及輸入的含義和解釋
  3. 輸入長度和緩衝區溢出
  • 針對緩衝區溢出編寫安全的程序代碼需要有一種心態,就是認爲任何輸入都存在危險,在處理輸入時不要使程序面臨危險。關於輸入的長度,這就意味着或者使用動態緩衝區確保有足夠的有效空間,或者把輸入數據處理成緩衝區長度的數據塊。即使使用動態緩衝區,程序員也一定要細心處理,保證請求的空間不會超出內存可用的範圍。一旦超出內存可用的範圍,程序必須能夠很好地處理這個錯誤。
  • 在緩衝區塊中處理輸入、拋棄多餘的輸入、中斷程序,或者任何其他的合理響應異常狀態的動作。
  1. 程序輸入的解釋
  • 程序輸入的數據可分爲文本和二進制兩種形式。
  • 當處理二進制數據時,程序假定將未經加工的一些二進制數據解釋爲整數、浮點數、字符串或者其他一些更復雜結構的數據。當讀入二進制數據時,這些假設的解釋必須進行驗證,如何進行處理的細節很多都取決於信息編碼的特殊解釋。
  • 程序將文本數據當作程序輸入進行處理。依據某些字符集,未經加工的二進制數據被解釋爲字符。傳統方式上假設的字符集是ASCII 字符集。
  • 除了識別輸入是什麼字符之外,更重要的是確定它們的含義。它們可以表示一個整數或者一個浮點數。它們也可能是一個文件名、一個URL地址、一個電子郵件地址,或者一些形式的標識符。
  1. 注入攻擊
  • 涉及多種對輸入數據進行無效處理的相關程序有關,特別是當程序輸入的數據有意或者無意間影響到程序的執行流的時候,這個問題就發生了。
  • 最常見的一種是,當輸入數據作爲一個參數傳遞給系統上的另一個輔助程序的時候,原來的程序會接着處理和使用它的輸出。
  • 命令注入攻擊:
    ```html

a)中顯示的一個perl CGI腳本。一個指定的用戶使用UNIX finger命令返回一些基本的細節。這個腳本放在Web服務器 個適當的位置,調用這個腳本能夠響應一個簡單的表單。
通過在服務器系統上運行一個程序,返回程序的輸出,如果有必要在 HTML Web頁裏對輸出的內容重新進行適當的格式化,調用腳本就能從服務器上取回想要得到的信息。
這個腳本存在一個致命的漏洞,用戶輸入的值作爲一個參數直接傳遞給finger程序。如果提供的是一個合法用戶的標識符。例如, lpb, 那麼接下會將會輸出這個用戶的信息。。然而,如果攻擊者提供 個值,其中包括shell元字符( shell元字符用於分離和組合多個命令 ),例如, xxx; echo attack success; ls -l finger *,輸出的信息就是c)中顯示的後一部分內容。
利用Web服務器的特權,攻擊者能夠運行系統上任何一個程序。
這就是一個命令窪入( command injection )攻擊,因爲使用的輸入數據可以建立一個命令,隨後通過擁有Web服務器特權的系統執行這個命令。
爲了應對這種攻擊,程序員需要明確識別有關輸入形式的任何假設,在使用數據之前驗證這些數據與其假設是否一致。驗證的時候通常將輸入的數據與描述假設形式的模式進行比較,一旦測試失敗就拒絕該輸入。

  • SQL注入攻擊

用戶提供的輸入數據可以建立一個SQL請求,從數據庫取回一些信息。
實例:
把一個名字當作輸入提供給腳本,這個名字從用戶提交的的表單中字段中讀取,使用這個值建立一個請求,從數據庫中取出與該姓名相關的記錄。這段代碼的漏洞與前面的命令注入例子非常類似,它們之間的不同僅僅在於SQL注入利用SQL元字符,而命令注入利用shell元字符。
在這裏插入圖片描述
如果輸入元字符Bob’ drop table suppliers= =,那麼運行結果將返回特定的數據庫記錄,但是接着將刪除整個表。
爲了阻止這類攻擊,所有輸入在使用之前必須進行驗證,任何元字符必須清除,消除它們的影響,或者完全拒絕元字符輸入。

  • 代碼注入攻擊

在這種攻擊中,提供的輸入包含被攻擊的系統能夠運行的代碼。
實例:
漏洞的產生是由於使用一個變量構造了一個文件名,這個文件隨後將被包含到該腳本中。
在這裏插入圖片描述
主腳本設置了變量$ path的值,該變量指的是包括這個程序、所有代碼和數據文件的主要路徑。PHP將原來在HTTP請求 中提供的任何輸入變量的值分配給同名的全局變量,一個毫無經驗的程序員可以很容易地編寫這種表單處理程序。,因而一個用戶能夠爲任意一個期望的全局變量隨意指定一些值,這些值能夠被建立並傳遞給腳本。
變量$ path不是一個表單字段, PHP的第二行是一個include命令,它不僅能包含本地文件,而且如果提供了一個URL ,那麼被包含的掘代碼可能源、自網絡上的任何地方。
利用一個與圖 12-4b相似的請求就可以實現代碼注入攻擊。這就導致變量$ path 包含一個文件的URL ,而這個文件是攻擊者的PHP代碼。同時變量$ path 也能定義另一個變量$cmd ,它告訴攻擊者腳本可以運行什麼命令。在這個例子中,附加的命令可以非常簡單地列出當前路徑下的所有文件。然而,這個命令也可以是Web服務器上的任何命令,只要擁有特權就能運行。
對策:
阻止將表單字段的值分配給全局變量。
在include (和require )命令中僅使用常量值。它能確保包含的代碼真正源於指定的文件。如果不得不使用一個變量,那麼在使用之前必須立即進行仔細的驗證。

  • 其他的注入攻擊,包括mail注入、格式化字符串(format string )注入和解釋器(interpreter )注入。

只要一個程序調用一些服務,而這些服務來自於另一個程序、服務或者函數,就可能發生注入攻擊;給一個程序傳遞
來源於外部的一些不可信的、沒有進行充分檢查和驗證的信息時,也可能發生注入攻擊。

  • 我們需要識別所有的輸入源,在使用這些輸入之前驗證所有的假設情況,以及理解那些提供給調用程序、服務和函數的數據值的含義和解釋。
  1. 跨站點腳本攻擊(XSS)
  • 一個用戶給程序提供輸入,而由此產生的結果輸出給另外一個用戶。因爲這種攻擊經常在腳本型的Web應用中看到,因此稱爲跨站點腳本攻擊。
  • 這個漏洞發生在網站應用程序接受用戶輸入數據卻沒有做必要的編碼,如果對用戶輸入的數據沒有進行正確的編碼和過濾,這個被注入惡意腳本將發送給其他用戶,對瀏覽器來說,它沒有辦法知道應不應該相信一個腳本的合法性,瀏覽器會正常地把這個腳本當作普通腳本執行,這個時候惡意的操作就不可避免的發生了,大部分的時候XSS是用來竊取cookie,或竊取用戶的會話令牌session,以此進行會話劫持。
  • cookie是用戶和服務器之間的橋樑,服務器可以使用session來保存用戶的身份信息(ID,購物車)等,但是需要訪問網頁的時候帶上想要的cookie,通過cookie中的特定值來識別session ID,才能把單獨用戶和單獨的session聯繫起來,cookie是有狀態HTTP交互的一種機制。
  • 瀏覽器的同源策略:同源是指域名,協議和端口號都相同,同源策略導致了XSS攻擊必須在我們希望的同一個域下觸發,例如攻擊者想要竊取在www.a.com下的cookie,那就必須在www.a.com這個域下的某一個頁面放置XSS代碼。
  • 反射型XSS:
    考慮如下代碼:
<html>
<head> 
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> 
<title>XSS</title> 
</head> 
<body> 
<form action="" method="get"> 
<input type="text" name="input">     
<input type="submit"> 
</form> 
<br> 
<?php 
$XssReflex = $_GET['input'];
echo 'output:<br>'.$XssReflex;
?> 
</body> 
</html> 

當我輸入< script > alert(‘xss’)</ script > ,可以看到瀏覽器成功彈窗,說明我們輸出的JavaScript代碼成功被執行了。
在這裏插入圖片描述
當我們輸入一個其他的js腳本時,例如document.cookie就可以成功的竊取用戶的cookie信息。

  • 存儲型XSS
    和反射性XSS的即時響應相比,存儲型XSS則需要先把利用代碼保存在比如數據庫或文件中,當web程序讀取利用代碼時再輸出在頁面上執行利用代碼。但存儲型XSS不用考慮繞過瀏覽器的過濾問題,屏蔽性也要好很多。
    流程:
    在這裏插入圖片描述
    考慮下面一段代碼:
<span style="font-size:18px;"><meta http-equiv="Content-Type" content="text/html;charset=utf-8"/>  
    <html>  
    <head>  
    <title>XssStorage</title>  
    </head>  
    <body>  
    <h2>Message Board<h2>  
    <br>
    <form action="XssStorage.php" method="post">  
    Message:<textarea id='Mid' name="desc"></textarea>  
    <br>  
    <br>  
    Subuser:<input type="text" name="user"/><br> 
    <br>
    <input type="submit" value="submit" onclick='loction="XssStorage.php"'/>  
    </form>  
    <?php  
    if(isset($_POST['user'])&&isset($_POST['desc'])){  
    $log=fopen("sql.txt","a");  
    fwrite($log,$_POST['user']."\r\n");  
    fwrite($log,$_POST['desc']."\r\n");  
    fclose($log);  
    }  
      
    if(file_exists("sql.txt"))  
    {  
    $read= fopen("sql.txt",'r');  
    while(!feof($read))  
    {  
        echo fgets($read)."</br>";  
    }  
    fclose($read);  
    }  
    ?>  
    </body>  
    </html></span>  

這個頁面採用POST提交數據,生成、讀取文本模擬數據庫,提交數據之後頁面會將數據寫入sql.txt,再打開頁面時會讀取sql.txt中內容並顯示在網頁上,實現了存儲型xss攻擊模擬。
當我們輸入時,頁面成功彈窗 ,關閉瀏覽器重新打開,仍會彈窗。因爲該輸入以及存儲在文件中。

  1. 驗證輸入語法
  • 輸入的數據一定要與輸入假設進行比較,僅僅接受有效的輸入,這是一個重要的原則。另一個原則是將輸入的數據和已知的危險數據值比較
  • 這類比較通常採用正則表達式(regular expression )完成。正則表達式是由一系列描述允許的輸入變化的字符構成的模式。在正則表達式中的一些字符是逐個處理的,與它們進行比較的輸入數據必須包含那些字符。其他的一些字符有特殊的含義,可以是各種字符集的詳細說明、字符的分類以及重複的字符。如果輸入的數據比較失敗,那麼就要遭到拒絕。這時,一個適當的錯誤信息將發送到輸入源,允許用戶改正或者重新輸入。
  • 輸入數據的多重編碼問題:

Unicode是一種編碼方案,Unicode是爲了解決傳統的字符編碼方案的侷限產生的,它爲每種語言的每個字符設定了統一併且唯一的二進制編碼,以滿足跨語言,跨平臺進行文本轉換,處理的要求.
這裏就有兩個嚴重的問題,第一個問題是,如何才能區別 Unicode 和 ASCII ?計算機怎麼知道三個字節表示一個符號,而不是分別表示三個符號呢?我們已經知道,英文字母只用一個字節表示就夠了,如果 Unicode 統一規定,每個符號用三個或四個字節表示,那麼每個英文字母前都必然有二到三個字節是0,這對於存儲來說是極大的浪費,文本文件的大小會因此大出二三倍,這是無法接受的
unicode的實現:uft-8編碼,如果一個字節則其最高位二進制爲0;如果多字節,其第一個字節從最高位開始,連續的二進制爲值爲1的個數決定了其編碼的字節數,其餘各字節均以10開頭。
在這裏插入圖片描述
斜槓字符“/”,在一個UNIX 文件名裏用於分隔路徑,在ASCII UTF-8 中都是十六進制值2F ”。 UTF-8 編碼允許冗餘,承認更長的編碼:“CO AF ”和“EO 80 AF ”。儘管有嚴格使用最短編碼的限制,但很多Unicode解碼器接受任何一個同等有效的序列。
有一類攻擊企圖給腳本提供一個絕對路徑的文件,實際上需要的僅是一個簡單的當前路徑下的文件名。我們通常檢查提供的文件名不以“/”開始,或者不包含任何“../”上一級路徑的表示形式。如果檢查的時候採用的只是斜槓的最短Unicode編碼,沒有檢查其他較長的Unicode 編碼,但是攻擊者使用的正好是這些編碼。
由於多重編碼的可能性,因此輸入的數據必須首先轉換成單一的、標準的、最小的表示形式,這個過程稱爲標準化( canonicalization ),它包含用一個通用的值代替那些等價的編碼。一且完成這個過程,輸入的數據就能夠與可接受的輸入值的一個單一表示進行比較。

  • 一種類型的數據強制轉換成另一種類型的數據可能發生問題。
    一個緩衝區長度可以用一個無符號整數表示,它將要和一個可接受的最大緩衝區長度進行比較,在比較的時候無符號值可以轉換成一個有符號值來處理,不同的語言可能有不同的處理方式。這就導致一個漏洞,因爲負數的最高位爲 1,但是最高位爲1的無符號整數又是一個較大的正數值。這樣攻擊者就能指定一個很大的輸入長度,這個很大的無符號型輸入長度與緩衝區的最大長度進行比較的時候是作爲負數處理的。一個負數明顯小於一個較小的正數,這說明比較是成功的。然而,使用緩衝區的時候輸入數據將會超出緩衝區的實際範圍,對輸入數據的錯誤處理產生了緩衝區溢出。
  1. 輸入的fuzzing技術
  • 這是一個軟件測試技術,它使用隨機產生的數據作爲程序的輸入進行測試。測試的輸入範圍很大,包括直接的文本或者圖形輸入、在一個Web和其他的分佈式服務上發出的隨機網絡請求,以及傳遞給標準庫函數或者系統函數的隨
    機參數值。測試的目的在於確定程序或者函數是否能夠正確處理所有的非正常輸入、程序是否受到破壞,以及程序失敗以後是否得到正確響應。
  • 對任意的程序、服務或者函數的輸入假設來說, fuzzing技術的主要優點就是簡單和自由,而且產生大量測試的費用非常低。更進一步的,這樣的測試可以幫助程序識別它的可靠性和安全性。
  • 儘管輸入數據能夠完全隨機產生,但它也能依據一些模板隨機產生。設計這樣的模板可以檢查可能存在的bug 。
  • fuzzing技術僅能識別簡單類型的輸入錯誤,如果一個bug僅僅在極特殊的輸入時才能夠觸發,那麼fuzzing技術就不可能找到它。

編寫安全的程序代碼


  1. 算法的正確實現
  • 如果一個算法沒有正確實現問題的所有情形和變化,那這就可能允許一些似乎合法的程序輸入觸發一些程序行爲,而這些程序行爲並不是程序應該完成的,因而給攻擊者提供了一些其他的能力。這個錯誤可能是由於對程序輸入不合適的解釋或者處理造成的,但它也可能是對本來有效的輸入沒有進行適當處理而引起的。
  • 一個總所周知的例子:TCP會話欺騙或hijack攻擊

攻擊的目標不是使服務器處於半開放連接狀態,而是欺騙服務器接收僞造源地址的數據包,其中源地址屬於可信的主機但數據包卻源於攻擊者的系統。如果攻擊成功,那就會說服服務器運行一些命令或者提供數據訪問,前提是這些服務是允許提供給可信主機的。
由於使用一個僞造的源地址,攻擊者沒有看到來自服務器的響應,所以他們不知道服務器提供的初始序列號。然而,如果攻擊者能夠正確猜到這個數字,那麼就可以建立一個ACK包發送到服務器,接下來假定與服務器的連接已經建立。服務器認爲任何後續的數據包都來源於可信的主機,並按照其分配的權限處理這些數據。這次攻擊的hijack變量一直等待,直到一些授權的外部用戶連接井登錄服務器。接下來攻擊者嘗試猜測序列號,使用僞造的內容模仿授權用戶發給服務器的下一個數據包。如果猜測的數字正確,那麼攻擊者使用授權用戶的訪問權限和訪問許可發出任何請求,服務器都會響應。
由於可信主機確認服務器沒有發送數據包,因此這個系統就假設存在一個網絡錯誤井發送一個reset (RST )數據包中斷這次連接。攻擊者必須確保,在可信主機系統發送中斷之前將攻擊數據包發送到服務器並且被處理,攻擊者在攻擊目標服務器的同時向可信主機系統發出一個拒絕服務(denial-of-sevice )攻擊,就可以達到這個目的。
由於很多TCP/IP 的實現所使用的初始序列號都容易猜測,這一缺陷爲這些攻擊提供了機會。另外,使用序列號能夠識別一個會話的所有數據包。 TCP/IP 的標準規定每一次連接使用一個新的、不同的序列號,這樣就能夠區分以前連接的數據包,這個序列號可能是一個隨機數.

  • 程序員經常在程序中故意設置一些用於檢測和調試的代碼,這是這個問題的另一種表現形式。儘管在程序開發過程中設置一些代碼是有效的,但在程序的產品發行版中也會經常遺留下這些代碼。至少,這些代碼會將一些不合適的信息透露給使用該程序的用戶。在最壞情況下,它能允許用戶繞過安全檢查或者程序的其他限制,完成其他一些不允許完成的動作。
  • 一個更進一步的例子涉及高級語言或者中級語言解釋器的實現。其假設是解釋器可以正確實現特定的程序代碼。充分反映語言語法的失敗會導致bug 的出現,這些bug可能被攻擊者利用。
  1. 保證機器語言與算法一致
  • 第二個關鍵問題涉及在一些程序設計語言中指定的算法和實現這個算法所運行的機器指令之間的一致性,這個問題是大多數程序員最容易忽略的問題。其假設是編譯器或者解釋器能真正產生或執行可以有效實現語言語句的代碼。
  • 一個惡意的編譯器,程序員能夠在其中包含一些指令,當它處理一些特殊的輸入語句的時候觸發附加的代碼。這些語句甚至包含了部分編譯器,所以當編譯器源代碼被編譯的時候,即使在從編譯器掠代碼中移除所有附加的代碼之後也能重新插入這些變化。
  1. 數據值的正確解釋
  • 在計算機的最底層,所有的數據都是以二進制位組的形式進行存儲。這些數據存儲在內存的字節中,而這些字節會形成一個較大的存儲單元,例如一個字( word )或者長字( longword )。
  • 一些語言接受比較自由的數據解釋,允許程序代碼明顯改變對數據值的解釋,廣泛使用的C語言就具有這個特性。特別是將一個變量的值解釋爲整數或者內存地址(指針)都可以,在它們之間進行轉換非常容易。
  • 針對這些錯誤最好的防禦方棒,是使用具有較強類型的編程語言。
  1. 內存的正確使用
  • 程序需要使用內存的時候就動態分配,使用之後隨即釋放。如果一個程序沒有正確管理這個過程,那後果可能是堆區的可用內存逐步減少,直至完全用盡。這被稱爲內存泄漏( memory leak ),一旦堆區的可用內存用盡,程序便會崩憤。這給攻擊者提供了一個明顯的機制來在這樣的程序中實現拒絕服務攻擊。
  • Java 和C++等其他的語言都是自動管理內存的分配和釋放。
  1. 阻止共享內存競爭條件的產生
  • 多個進程或一個進程的多個線程訪問通用共享內存的管理。如果沒有恰當的訪問同步機制,那麼這些進程或線程由於重疊訪問、使用和替代共享值,就可能導致數據值被破壞或修改丟失。當多個進程或線程通過競爭來獲取對一些資源的未加控制的訪問時,會導致競爭條件( race condition )的發生
  • 如果我們選擇了不正確的同步原語序列,那就有可能造成各種進程或線程的死鎖(deadlock ),這時每個進程或線程都在等待訪問一個資源,而這個資源正在被其他的進程或線程訪問。如果不中斷其中的一個或者多個程序,就沒有更簡單的方怯從死鎖中恢復。攻擊者能夠在存在漏洞的程序裏觸發一個死鎖,實現拒絕服務攻擊。

與操作系統和其他程序進行交互


  1. 計算機程序模型的第三個部分,是程序在操作系統的控制下在計算機系統上執行。
  2. 當一個程序運行時,操作系統構造一個進程的執行環境。除了程序代碼和程序使用的數據外,該進程還包括一些操作系統提供的信息。這些信息包括用來適應程序操作的環境變量,以及爲該程序指定的命令行參數。所有這些數據應被視爲程序的外部輸入,所以在使用它們之前必須保證其有效性.
  3. 環境變量
  • 環境變量是操作系統中一個具有特定名字的對象,它包含了一個或多個應用程序所將用到的信息。
  • 操作系統在構造進程的內存空間時,將這些信息包含其中。默認情況下,進程拷貝父進程的環境變量值。然而,執行新程序的請求可以指定使用一系列新值。程序可以在任何時候修改進程的環境變量,這些修改依次傳遞給進程所產生的子進程。
  • 常見的環境變量:
    path環境變量:當要求系統運行一個程序而沒有告訴它程序所在的完整路徑時,系統除了在當前目錄下尋找此程序外,還應到path中指定的路徑中去找,用戶通過設置環境變量來更好的運行進程。
    LD_LIBRARY_PATH:該環境變量主要用於查找動態連接庫時處默認路徑之外的其他路徑。
    IFS:定義了shell用作字段分隔符的一系列字符,默認情況下會將下列字符當作字符分隔符:空格,製表符,換行符。
  • 這段簡單的腳本調用兩個不同的程序: sed和grep 。程序員假設這兩個程序的標準系統版本會被調用。但此處只是指定了文件名。爲了找到具體的程序,shell 將到PATH變量指定的目錄中查找對應的文件名。攻擊者只需要簡單地重定義變量PATH,使其包含攻擊者控制的目錄,比如該目錄中包含一個grep程序。當腳本運行時,攻擊者的程grep 代替程序的標準系統版本被調用。這樣這個程序就可以按照攻擊者的意願做任何事,因爲它具有shell 腳本所具有的權限。
    在這裏插入圖片描述
    對策:可以對每個程序使用絕對路徑名。變量PATH可以被重設爲腳本已知的默認值。
    採用這些方法後,這個版本的腳本程序也還有漏洞,這次是因爲IFS 變量。該變量被用來區分一行命令中的不同單詞。默認情況下爲空格、製表符或換行符。然而,可以將它設置成任意的字符序列。我們考慮在該集合中包含“=”的影響。接下來給變量PATH賦一個新值被解釋成執行程序PATH的命令,而後面的目錄列表被視爲程序參數。如果攻擊者也修改變量PATH加入了包含攻擊程序PATH的目錄,那麼當腳本運行時,攻擊程序就會被執行。
    如果需要一個腳本應用程序,那最好的解決方法是使用一個編譯過的包裝函數(wrapper function )調用它。在調用需要的腳本程序前,一個編譯好的程序可以構建一個相對安全的環境變量集合,然後使用該程序完成屬主或組的改變。
    儘管以高優先級運行的是編譯過的程序,但是攻擊者仍然可能利用環境變量進行攻擊。如果這個程序執行另一個程序,依據執行時使用的命令,變量PATH仍然被用於尋找將要執行的程序。因而,所有這類程序必須首先將其重設爲已知的安全值。
    環境變量LD LIBRARY_PATH的功能。該變量的使用對動態庫的使用提供了一定的靈活度。但同樣它也引入了一些可能的攻擊機制。攻擊者構造一個通用庫的自定義版本,將已知的、當程序執行時能被動態鏈接的庫函數的代碼替換成攻擊代碼。通過設置變量LD_LIBRARY_PATH ,程序將首先引用含有攻擊代碼版本的庫函數,當程序執行的時候調用該庫函數,攻擊代碼就會利用目標程序的特權運行。
  1. 使用最小特權原則
  • 攻擊者能夠利用受到攻擊的程序或服務的特權和訪問權限來執行代碼。如果這些特權比本來爲攻擊者分配的權
    限高,就會導致特權擴大。
  • 每個程序都應該使用完成其功能所需的最小特權。
  • 正常情況下,當用戶運行一個程序的時候,該程序應該和用戶具有相同的優先級和訪問權限。很多情況下,程序需要使用一些該用戶未被授權訪問的資源。通常的做越是對一個服務採用一個專門的系統登錄,並且僅登錄者可以訪問該服務所使用的目錄和文件。實現該服務的任何一個程序都使用系統用戶的訪問權限運行,這樣的程序被視爲特權程序。
  • 另一個需要關心的問題是保證任何特權程序僅能修改所需的文件和目錄。
  • 和特權程序相關的最大的安全問題,出現在程序以root或者管理員權限運行的時候。這時該程序對系統擁有很高的訪問和控制權限。獲取這樣的權限正是系統攻擊者的主要目的,因而這些特權程序也成了攻擊者的主要目標。最小特權原則要求訪問權限應該儘可能地小,時間也應儘可能地短。
  • 現在我們考慮良好的防禦性程序設計準則,大的複雜程序應儘量分解成小模塊,每個模塊僅在需要的時候擁有相應的訪問權限。這種程序的模塊化設計思路使得模塊間的隔離度( degree of isolation )更大,從而也降低了在一個組件中的安全問題的影響範圍。
  • 最小化特權的更進一步的技術,是在一個特定分隔出的與文件系統隔離的區域內運行存在潛在漏洞的程序。
  1. 系統調用和標準庫函數
  • 程序通過操作系統調用來使用系統資源,通過調用標準庫函數完成通常的操作。
  • 操作系統和庫函數的作用是管理資源,目標是爲在系統上運行的程序提供最好的性能。這使得服務請求可以通過緩存、重新排序或其他的修改對系統的使用進行優化。
  • 實例:
  • 安全的文件粉碎是指刪除一個文件後其內容不能恢復。我們建議利用多個不同的位模式(bit pattern )反覆覆蓋數據內容,使得與原來的數據具有較大的差異。因此,一個好的文件粉碎程序可以實現圖a所示的算法。

存在的問題:
在文件以寫的方式打開時,系統需要將新的數據當作原來的數據寫到相同的磁盤塊。
當文件被某個樣本覆蓋的時候,數據立即寫入硬盤。第一步是將數據拷貝到一個應用程序的緩衝區中,由標準庫文件I/0例程管理。這些例程控制着緩衝區的寫人,直到緩衝區充滿之後,程序刷新緩衝區,或者關閉文件。如果文件比較小,在程序循環執行、返回到文件的開始以及寫入新的樣本之前緩衝區不會充滿。
當刷新I/0緩衝區以及關閉文件的時候,數據被寫入硬盤。然而,在操作系統的文件處理代碼中還有另外一層緩衝,這一層緩衝區存儲在操作系統上當前正在運行的所有進程讀寫文件的信息,它對這些讀寫的數據進行重組或調度,使其更利於物理設備的高效訪問。即使程序把來自應用程序的緩衝區的數據刷新到文件系統的緩衝區,數據將不會被立即寫入。如果新的替代數據被刷新,那麼它們很可能再次替代原來未被寫入磁盤的數據,因爲文件系統代碼將假設早先的值不再需要,而且該替代數據也可能沒有被寫入硬盤。因此程序必須強制文件系統中的數據與設備上的值同步,才能確保數據被實際地傳送到設備上。

  1. 阻止共享資源的競爭條件產生
  • 常用的技術是在共享的文件上設定一個鎖 (lock ),保證每個進程輪流訪問
  • 文件鎖( lockfile ):一個進程必須創建和擁有文件鎖,這樣纔可以獲取對共享資源的訪問。任何檢測到文件鎖存在的其他進程必須等待,直到該文件鎖被撤銷,它才能創建自己的文件鎖來獲取訪問權。
  • 首先程序要檢查文件鎖,如果不存在就產生一個。遺憾的是這包含一個致命的缺陷。假設有兩個進程,每個進程都試圖檢查併產生這個文件鎖。第一個進程檢查井確認文件鎖不存在,然而,在它產生文件鎖之前,系統將該進程掛起而讓其他進程運行。而在此時,另一個進程也檢查到文件鎖不存在併產生一個,並進一步使用共享資源。接着第二個進程也被掛起,控制返回第一個進程,第一個進程也將產生文件鎖,進一步同時訪問共享資源,這時在共享文件中的數據遭到破壞。這是競爭條件的最傳統的解釋。問題是檢查文件鎖不存在和產生文件鎖的過程必須是同時進行的,沒有中斷的可能,這種操作被稱爲原子操作( atomic operation )。
  1. 安全臨時文件的使用
  • 很多程序在處理數據的時候,需要存儲數據的臨時副本。臨時文件通常用於這一目的。
  • 有關臨時文件的關鍵問題是,它們應該是唯一的並且不能被其他進程訪問。在某種意義上講,這與管理共享文件的訪問是對立的。構造文件名的一般技術是在名字中包含一個值,例如進程標識符( process identifier )。由於每個進程有不同的標識符,這就保證了文件名的唯一性。程序通過檢查確認文件不存在,然後產生臨時文件。
  • 攻擊者不會依據規則行事。他們試圖猜測某些特權程序將要使用的臨時文件名,在程序檢查文件不存在和產生臨時文件的兩個動作之間試圖產生一個臨時文件。
  • 安全臨時文件的產生和使用更需要使用隨機的臨時文件名。文件名的產生應該使用原子操作,正如文件鎖的產生過程那樣。
  • 對文件實現最小訪問也是非常重要的,在大多數情況下,只有創建該文件的程序的所有者纔可以訪問。
  • 當在共享的臨時目錄中創建文件時,其權限應指定爲僅文件的所有者或者系統管理員可以刪除。
  1. 與其他程序的交互
  • 除了使用操作系統和標準庫函數提供的功能之外,程序也可以使用其他程序提供的服務。
  • 與保護多個程序問數據流的機密性和完整性有關。當多個程序運
    行在同一個計算機系統上時,合理使用比如管道和臨時文件等系統功能可以提供這樣的保護。如果程序運行在不同的系統上,且系統間通過網絡相連,那麼這些網絡連接應該使用相應的安全機制。可供選擇的安全機制有IP Security (IPSec )、 Transport Layer/Security Socket Layer Security ( TLS/SSL )或Secure Shell (SSH )連接。
  • 從安全的角度講,檢測和處理程序交互中產生的異常和錯誤也很重要。當一個進程調用另一個程序作爲子進程時,父進程必須保證子進程正常終止井接受它的退出狀態。進程還必須捕獲井處理與其他程序或操作系統交互過程中產生的信號。

處理程序的輸出


實例:
  • 第一個例子涉及使用的純文本終端。這些終端通常支持一系列的功能鍵,編寫這個程序要求當按下這些功能鍵的時候可以發送任何需要的字符序列,程序通過發送一個特定的轉義(escape )序列實現。終端識別這些序列,不是將這些字符顯示出來,而是執行請求的操作。
  • 攻擊者操縱正常用戶在其終端上顯示一些經過仔細設計的文本。這可以通過誘使正常用戶運行一個程序實現,比如將程序包含在電子郵件中,或者直接寫在其終端上,如果用戶允許的話。除了顯示一些無關的信息擾亂正常用戶外,文本中還可以包含一些轉移序列,首先編程實現發送特定命令的功能鍵,接着是發送文本的命令,就好像經編程的功能鍵被按下一樣。如果顯示文本的程序不久就退出了,那麼它發送的對應特定功能鍵的文本就被視爲目標用戶鍵入的下一個命令。因而,攻擊者可以完成該用戶的所有操作,可能包括刪除用戶文件或更改用戶的口令。
  • 我們吸取的主要教訓與用戶期望的發送到用戶終端顯示的輸出數據的類型有關。另一個教訓是要確保不可信資源不允許直接輸出到用戶界面上
  • 第三個例子,是利用一些Web服務器上的留言板進行的跨站點腳本(XSS )攻擊。如果該留言板應用程序沒有充分檢查和清理用戶提供的輸入,那麼接下來在用戶瀏覽這些留言時這個輸入能夠用於實現一次攻擊。
  • 是不同的字符集允許對元字符的不同編碼方式,這會改變對有效輸出的解釋。如果特定的顯示程序或設備對於採用的具體的編碼方式並不知情,那麼就可能引發對程序的不同假設,從而可能導致過濾失敗。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章