|
網(wǎng)絡(luò)技術(shù)是從1990年代中期發(fā)展起來(lái)的新技術(shù),它把互聯(lián)網(wǎng)上分散的資源融為有機(jī)整體,實(shí)現(xiàn)資源的全面共享和有機(jī)協(xié)作,使人們能夠透明地使用資源的整體能力并按需獲取信息。資源包括高性能計(jì)算機(jī)、存儲(chǔ)資源、數(shù)據(jù)資源、信息資源、知識(shí)資源、專家資源、大型數(shù)據(jù)庫(kù)、網(wǎng)絡(luò)、傳感器等。 當(dāng)前的互聯(lián)網(wǎng)只限于信息共享,網(wǎng)絡(luò)則被認(rèn)為是互聯(lián)網(wǎng)發(fā)展的第三階段。 檢查代碼中的安全性缺陷,是軟件創(chuàng)建過(guò)程中的一個(gè)關(guān)鍵要素,它與計(jì)劃、設(shè)計(jì)和測(cè)試同等重要。作者經(jīng)過(guò)數(shù)年的代碼安全性檢查總結(jié)出了標(biāo)識(shí)模式和最佳做法,開(kāi)發(fā)人員可以按照該總結(jié)來(lái)捕獲潛在的安全漏洞。首先,要檢查代碼運(yùn)行的環(huán)境,考慮運(yùn)行代碼的用戶的角色,以及研究代碼可能存在的任何歷史安全性問(wèn)題。在理解這些背景問(wèn)題之后,就可以搜尋特定的安全漏洞了,包括 SQL 注入式攻擊、交叉站點(diǎn)腳本和緩沖區(qū)溢出。此外,可以搜尋并修改某些紅色標(biāo)記(如變量名“password”、“secret”)和其他明顯但常見(jiàn)的安全性錯(cuò)誤。我的大部分工作就是檢查別人的代碼,尋找安全性錯(cuò)誤。不可否認(rèn),這并不是我的首要任務(wù)(我的首要任務(wù)是設(shè)計(jì)檢查和威脅建模),但是,我的確因此而接觸到了大量的代碼。 希望您明白,檢查其他人的代碼是一件好事的同時(shí),卻并不是創(chuàng)建安全軟件的方式。通過(guò)設(shè)計(jì)、編寫(xiě)、測(cè)試以及編寫(xiě)有關(guān)安全系統(tǒng)的文檔,然后安排時(shí)間進(jìn)行安全性檢查、培訓(xùn)和工具使用,才可以生產(chǎn)出安全的軟件。僅僅依靠設(shè)計(jì)、編寫(xiě)、測(cè)試以及編寫(xiě)有關(guān)項(xiàng)目的文檔,然后尋找安全性錯(cuò)誤并不能創(chuàng)建安全的軟件。代碼檢查只是該過(guò)程的一部分,其自身不能創(chuàng)建安全的代碼。 在本文中,我不對(duì)代碼安全漏洞(例如,整數(shù)溢出攻擊、SQL 注入式攻擊以及緩沖區(qū)溢出)加以論述;您可以通過(guò)閱讀書(shū)籍(如我撰寫(xiě)的 Writing Secure Code,Microsoft Press®,2002)來(lái)了解這些問(wèn)題。但是,我將從一個(gè)較高的角度來(lái)審視代碼檢查過(guò)程中要注意的問(wèn)題。在正式開(kāi)始之前,盡管我希望指出這就是我檢查代碼中安全性錯(cuò)誤的方式,但是您沒(méi)有必要以這種方式來(lái)檢查代碼,而且我也不能保證這就是檢查某種缺陷的最完美形式。在查看代碼時(shí),我會(huì)記錄下自己頭腦中的想法,希望這些想法對(duì)您會(huì)有所幫助。 我認(rèn)為有三種方法可用于檢查代碼:深入分析、快速分析以及混合方法。我傾向于將重點(diǎn)放在混合方法上,因?yàn)樗膬?yōu)勢(shì)在于可快速覆蓋大部分范疇;如果我認(rèn)為某些地方需要進(jìn)行更深入的分析,我會(huì)做個(gè)標(biāo)記以便稍后對(duì)它進(jìn)行代碼檢查,這可能會(huì)牽涉到相關(guān)領(lǐng)域其他的專家提示。但是現(xiàn)在,讓我來(lái)介紹一下最初的快速代碼檢查,我喜歡稱之為 Sweep“n”Tag 方法(掃描與標(biāo)記方法),該方法快速掃描代碼并標(biāo)記要求進(jìn)一步檢查的代碼。以下是我的這一過(guò)程的概要。 分配時(shí)間和精力 我使用一個(gè)優(yōu)先級(jí)系統(tǒng)來(lái)確定檢查代碼需要花費(fèi)的相關(guān)時(shí)間。該系統(tǒng)根據(jù)潛在的損壞程度建立,這種潛在的損壞程度體現(xiàn)為安全漏洞是否被利用并且是否存在可能的攻擊。配額系統(tǒng)基于下列特征: 默認(rèn)情況下代碼是否運(yùn)行? 是否利用提升的權(quán)限運(yùn)行代碼? 是否對(duì)網(wǎng)絡(luò)接口進(jìn)行代碼偵聽(tīng)? 網(wǎng)絡(luò)接口是否未經(jīng)身份驗(yàn)證? 代碼是否以 C/C++ 編寫(xiě)? 代碼是否具有安全漏洞的歷史? 安全性研究人員仔細(xì)是否審查過(guò)該組件? 代碼是否處理敏感或者專有數(shù)據(jù)? 代碼是否可重復(fù)使用(例如,DLL、C++ 類標(biāo)頭、庫(kù)或者程序集)? 根據(jù)威脅模型,該組件是處于高風(fēng)險(xiǎn)環(huán)境中,還是易于遭受很多高風(fēng)險(xiǎn)威脅? 如果我從該列表得到多于三個(gè)或四個(gè)肯定的回答,那么我將對(duì)代碼進(jìn)行更進(jìn)一步的檢查。事實(shí)上,如果代碼對(duì)傳輸控制協(xié)議 (TCP) 或用戶數(shù)據(jù)報(bào)協(xié)議 (UDP) 套接字進(jìn)行偵聽(tīng),并且在默認(rèn)情況下處于運(yùn)行狀態(tài),那么請(qǐng)準(zhǔn)備花費(fèi)大量時(shí)間來(lái)檢查代碼。 在尋找安全性錯(cuò)誤的過(guò)程中,我傾向于檢查三種主要代碼類別:C/C++ 代碼、Web 服務(wù)器應(yīng)用程序代碼(例如,ASP、ASP.NET、CGI 和 Perl )以及托管代碼(主要是C#,還有若干 Visual Basic®.NET)。 應(yīng)該注意,每種語(yǔ)言之間都有一些細(xì)微的差異。首先,關(guān)于 C 和 C++ 的首要問(wèn)題是緩沖區(qū)溢出。不可否認(rèn),還有其他問(wèn)題存在,但是在同一句話中聽(tīng)到“緩沖區(qū)”和“溢出”這些單詞時(shí),您幾乎就可以確認(rèn)這是涉及到 C 或 C++ 了。高級(jí)語(yǔ)言(例如,C#、Visual Basic .NET 和 Perl)應(yīng)該沒(méi)有緩沖區(qū)溢出問(wèn)題。如果有,錯(cuò)誤可能存在于運(yùn)行時(shí)環(huán)境,而不在檢查的代碼中。然而,這些語(yǔ)言通常會(huì)用于 Web 服務(wù)器應(yīng)用程序代碼中,而且會(huì)面臨其他類型的缺陷。緩沖區(qū)溢出最令人討厭,因?yàn)楣粽呖梢詫⒋a注入到運(yùn)行進(jìn)程中并對(duì)它進(jìn)行襲擊。因此,讓我們首先看看緩沖區(qū)溢出。 C 和 C++ 中的緩沖區(qū)溢出 緩沖區(qū)溢出是軟件行業(yè)中的棘手問(wèn)題,您應(yīng)該盡最大的努力將它們從您的代碼中根除。然而,最好是首先確保它們沒(méi)有進(jìn)入到代碼中。 我有兩種檢查緩沖區(qū)溢出的方法。第一種,標(biāo)識(shí)所有應(yīng)用程序的入口點(diǎn),尤其是網(wǎng)絡(luò)入口點(diǎn),同時(shí)跟蹤在代碼中移動(dòng)的數(shù)據(jù)并審查數(shù)據(jù)的處理方式。我假定所有的數(shù)據(jù)格式都不正確。當(dāng)查看涉及數(shù)據(jù)(讀取數(shù)據(jù)或?qū)懭霐?shù)據(jù))的任何代碼時(shí),我會(huì)問(wèn)“是否存在可導(dǎo)致該代碼失敗的數(shù)據(jù)版本呢?”這種方法檢查得很徹底,但是很費(fèi)時(shí)。另一種方法是尋找已知的和潛在危險(xiǎn)的構(gòu)造并跟蹤數(shù)據(jù)回到入口點(diǎn)。例如,請(qǐng)看下面的代碼: void fuction(char *p) { char buff[16]; ••• strcpy(buff,p); ••• } 如果我看到這樣的代碼,我將跟蹤變量 p 返回到它的源,如果它來(lái)自于我不信任的位置,或者它所復(fù)制的來(lái)源的有效性未經(jīng)檢查,那么我就知道我發(fā)現(xiàn)了一個(gè)安全性錯(cuò)誤。請(qǐng)注意,strcpy 本身并不危險(xiǎn)或不安全。而危險(xiǎn)的是使函數(shù)變得如此可怕的數(shù)據(jù)。如果檢查數(shù)據(jù)格式正確,則 strcpy 可以是安全的。當(dāng)然,如果您判斷出錯(cuò),就會(huì)有安全性錯(cuò)誤了。我也檢查了“n”字符串操縱函數(shù)(例如 strncpy),因?yàn)槟需要檢查緩沖區(qū)大小計(jì)算是否正確。 我對(duì)處理標(biāo)記文件格式的代碼比較提防。我指的標(biāo)記文件由幾個(gè)塊區(qū)組成,每個(gè)塊區(qū)都具有說(shuō)明下一塊區(qū)數(shù)據(jù)的標(biāo)頭。MIDI 音樂(lè)格式就是一個(gè)很好的例子。在名為 quartz.dll(處理 MIDI 文件)的 Windows 組件中發(fā)現(xiàn)并修改了一個(gè)嚴(yán)重的安全性錯(cuò)誤。格式錯(cuò)誤的 MIDI 構(gòu)造將導(dǎo)致處理文件的代碼失敗,甚至更糟糕。您可以在 Unchecked Buffer in DirectX Could Enable System Compromise 中閱讀有關(guān)該錯(cuò)誤的詳細(xì)信息。 我要注意的另一個(gè)構(gòu)造如下所示: while (*s != '\\') *d++ = *s++; 這個(gè)循環(huán)受到源中字符的限制,但不受目標(biāo)大小的限制;旧希沂褂孟旅娴恼齽t表達(dá)式來(lái)掃描 *x++ = *y++: \*\w+\+\+\s?=\s?\*\w+\+\+ 當(dāng)然,也可以使用 *++x = *++y,因此您也需要對(duì)它進(jìn)行掃描。我想再一次強(qiáng)調(diào),該構(gòu)造沒(méi)有危險(xiǎn)(除非源不受信任),因此您需要確定源數(shù)據(jù)的可信性。 接下來(lái)應(yīng)該注意的是另一個(gè)與緩沖區(qū)溢出相關(guān)的問(wèn)題:整數(shù)溢出漏洞。 C 和 C++ 中的整數(shù)溢出 如果您不知道整數(shù)溢出攻擊是什么以及如何修復(fù)它們,那么您應(yīng)該首先閱讀我的文章“Development Impacts of Security Changes in Windows Server 2003”。當(dāng)執(zhí)行算法以計(jì)算緩沖區(qū)大小并且計(jì)算導(dǎo)致上溢或下溢時(shí),真正的安全性漏洞會(huì)隨同這些缺陷一起出現(xiàn)。請(qǐng)看下面的示例: void func(char *b1, size_t c1, char *b2, size_t c2) { const size_t MAX = 48; if (c1 + c2 > MAX) return; char *pBuff = new char[MAX]; memcpy(pBuff,b1,c1); memcpy(pBuff+c1,b2,c2); } 上面的代碼看起來(lái)沒(méi)有問(wèn)題,但如果將 c1 和 c2 相加,結(jié)果超過(guò) 232-1,您就會(huì)意識(shí)到有問(wèn)題了。例如,0xFFFFFFF0 和 0x40 相加的結(jié)果為 0x30(十進(jìn)制為 48)。當(dāng)這些值用于 c1 和 c2 時(shí),加起來(lái)的和可以通過(guò)大小檢查,然后代碼會(huì)將大約 4GB 復(fù)制到 48 個(gè)字節(jié)的緩沖區(qū)。這樣就會(huì)出現(xiàn)緩沖區(qū)溢出!類似于這樣的許多錯(cuò)誤都可以被利用,使攻擊者將代碼注入您的進(jìn)程中。 當(dāng)檢查 C 和 C++ 代碼的整數(shù)溢出問(wèn)題時(shí),我將查找運(yùn)算符 new 和動(dòng)態(tài)內(nèi)存分配函數(shù)(alloca、malloc、calloc、HeapAlloc 等等)的所有實(shí)例,然后確定如何計(jì)算緩沖區(qū)大小。然后,我會(huì)詢問(wèn)自己下列問(wèn)題: 這些值可以超過(guò)特定的最大值嗎? 這些值可以小于零嗎? 數(shù)據(jù)是否被截?cái)啵▽?32 位值復(fù)制到 16 位值中,然后復(fù)制 32 位大。? [page_break] 我的一位 Microsoft 同事的經(jīng)驗(yàn)是:如果在用于比較的表達(dá)式中執(zhí)行數(shù)學(xué)運(yùn)算,那么就有可能上溢或下溢數(shù)據(jù)。如果將計(jì)算用于確定緩沖區(qū)大小,情況會(huì)更加糟糕,尤其是,如果一個(gè)或多個(gè)緩沖區(qū)大小計(jì)算元素已經(jīng)被攻擊者破壞。 任何語(yǔ)言中的數(shù)據(jù)庫(kù)訪問(wèn)代碼 作為一般規(guī)則,開(kāi)發(fā)人員以高級(jí)語(yǔ)言(如 C#、scripting 語(yǔ)言以及類似的語(yǔ)言)編寫(xiě)數(shù)據(jù)庫(kù)應(yīng)用程序。相對(duì)而言,很少以 C 和 C++ 編寫(xiě)數(shù)據(jù)庫(kù)代碼,但是有些人使用各種 C/C++ 類庫(kù),例如 MFC 中的 CDatabase 類。 其中可以檢測(cè)到兩個(gè)問(wèn)題。第一個(gè)是連接字符串,包括硬編碼的密碼或者使用管理員帳戶的連接?梢詸z測(cè)到的第二個(gè)問(wèn)題是 SQL 注入式攻擊漏洞。 當(dāng)我查看托管代碼時(shí),首要的事情就是搜索 System.Data 命名空間的所有代碼,尤其是System.Data.SqlClient。當(dāng)我看到這些時(shí),就要格外謹(jǐn)慎了!接下來(lái),我在代碼中查找如“connect”這樣的單詞(它通常出現(xiàn)在連接字符串旁)。該連接字符串有兩個(gè)需要關(guān)注的屬性:連接 id(通常寫(xiě)為 uid)和密碼(通常寫(xiě)為 pwd)。這些都是潛在的安全漏洞: DRIVER={SQL Server};SERVER=hrserver;UID=sa;PWD=$esame 事實(shí)上,在上面的示例中有兩個(gè)錯(cuò)誤。第一,以系統(tǒng)管理員帳戶 sa 進(jìn)行連接;這違背了授予最低權(quán)限這一必要原則。代碼永遠(yuǎn)不應(yīng)該以系統(tǒng)管理員帳戶連接到數(shù)據(jù)庫(kù),因?yàn)楫?dāng)該帳戶被惡意用戶使用時(shí),它會(huì)給數(shù)據(jù)庫(kù)帶來(lái)災(zāi)難性后果。第二,密碼是硬編碼的。有兩個(gè)理由可以說(shuō)明這是錯(cuò)誤的:第一個(gè)理由,密碼會(huì)被發(fā)現(xiàn);其次,如果更改密碼,又該如何處理?(您將必須更新所有客戶端。) 接下來(lái)的主題是 SQL 注入式攻擊。SQL 注入的癥結(jié)在于使用字符串連接來(lái)構(gòu)建 SQL 語(yǔ)句。當(dāng)掃描代碼時(shí),我將查看 SQL 語(yǔ)句的創(chuàng)建位置。一般而言,這涉及搜索諸如“update”、“select”、“insert”、“exec”以及任意我知道使用的表名或數(shù)據(jù)庫(kù)名之類的單詞。為了幫助解決問(wèn)題,我使用下面的 ildasm.exe 來(lái)審查托管程序集: ildasm /adv /metadata /out:file test.exe 然后,在生成的輸出中分析“User Strings”部分。如果發(fā)現(xiàn)任何使用字符串連接的數(shù)據(jù)庫(kù)查詢,那么這就是一個(gè)潛在的安全缺陷,必須使用參數(shù)化查詢來(lái)對(duì)其進(jìn)行修復(fù)。 使用字符串連接構(gòu)建存儲(chǔ)過(guò)程也不能防止 SQL 注入。簡(jiǎn)而言之,字符串連接加上 SQL 語(yǔ)句會(huì)使情況變?cè),而字符串連接加上 SQL 語(yǔ)句再加上系統(tǒng)管理員帳戶就無(wú)異于一場(chǎng)災(zāi)難。 任意語(yǔ)言中的 Web 頁(yè)代碼 基于 Web 頁(yè)的應(yīng)用程序中最常見(jiàn)的錯(cuò)誤是跨站點(diǎn)腳本 (XSS) 問(wèn)題。盡管我也會(huì)查找其他問(wèn)題(例如 SQL 注入和拙劣的加密),但是 XSS 錯(cuò)誤相當(dāng)普遍。核心 XSS 漏洞可能會(huì)在受害者的瀏覽器中顯示不受信任的用戶輸入,所以我首先搜索所有將數(shù)據(jù)發(fā)送給用戶的代碼構(gòu)造。例如,在 ASP 中查找 Response.Write 和 <%= %> 標(biāo)記。接下來(lái),分析被寫(xiě)入的數(shù)據(jù)從而查看它的來(lái)源。如果該數(shù)據(jù)來(lái)自 HTTP 實(shí)體(例如,窗體或查詢字符串),并且沒(méi)有檢查有效性就將它發(fā)送到用戶的瀏覽器,那么就會(huì)存在 XSS 錯(cuò)誤。下面是一個(gè)非常簡(jiǎn)單,但又最為常見(jiàn)的 XSS 示例: Hello, <% Response.Write(Request.QueryString("Name")) %> 正如您看到的那樣,“Name”參數(shù)被發(fā)送回用戶,而沒(méi)有首先檢查它是否有效及格式規(guī)范。 任意一種語(yǔ)言中的機(jī)密與加密 一些開(kāi)發(fā)人員喜歡在代碼中存儲(chǔ)機(jī)密數(shù)據(jù)(例如,密碼和加密密鑰),并創(chuàng)建自己的不可思議的密碼算法。請(qǐng)不要這么做! 我首先尋找變量名和名稱中包含“key”、“password”、“pwd”、“secret”、“cipher”以及“crypt”的函數(shù)。任何內(nèi)容都需要加以分析。您可能經(jīng)常獲得貌似正確但實(shí)際錯(cuò)誤的“密鑰”,但是要注意其他幾項(xiàng),它們也許會(huì)產(chǎn)生嵌入式的機(jī)密數(shù)據(jù)或“不可思議的”加碼系統(tǒng)。搜索密碼算法的同時(shí),我也尋找 XOR 運(yùn)算符,因?yàn)樗鼈兘?jīng)常用于加密。最糟糕的代碼是使用嵌入式密鑰來(lái) XOR 數(shù)據(jù)流的代碼! Visual Basic 和 C++ 中的 ActiveX 控件 當(dāng)我檢查新的 ActiveX® 控件時(shí),我始終想問(wèn)一個(gè)問(wèn)題:為什么不使用托管代碼來(lái)編寫(xiě)?我問(wèn)這個(gè)問(wèn)題的原因在于,托管代碼允許部分信任方案,而 ActiveX 卻不是。 接下來(lái),我分析控件的所有方法和屬性(.IDL 文件是進(jìn)行該操作最好的切入點(diǎn)),并且將自己設(shè)想為一個(gè)進(jìn)行惡意攻擊的人。我能利用這些方法或者屬性來(lái)做一些什么樣的危險(xiǎn)事情呢?通常,很多方法以“VerbNoun”格式(動(dòng)詞 + 名詞)進(jìn)行命名,例如 ReadRegistry、WriteFile、GetUserName 和NukeKey,所以我尋找發(fā)音復(fù)雜的動(dòng)詞和屬于敏感資源的名詞(資源)。 例如,如果攻擊者可以訪問(wèn)用戶硬盤(pán)上的任何文件,并且可以將它發(fā)送到任意位置(例如,攻擊者控制下的 Web 站點(diǎn)),那么 SendFile 方法就存在潛在的危險(xiǎn)!任何訪問(wèn)用戶計(jì)算機(jī)上的資源的操作都需要進(jìn)一步的審查。 如果該控件被標(biāo)記為可安全編寫(xiě)腳本 (SFS),我會(huì)進(jìn)行額外的檢查工作,因?yàn)樵摽丶赡軙?huì)在 Web 瀏覽器中在不警告用戶的情況下被調(diào)用。如果該控件在安裝時(shí)執(zhí)行 ATL IobjectSafetyImpl 接口或設(shè)置下面的“可安全編寫(xiě)腳本”或“可安全激活”實(shí)現(xiàn)類別,則您可以確定它是否為 SFS: [HKEY_CLASSES_ROOT\CLSID\<GUID>\Implemented Categories\{7DD95801-9882-11CF-9FA9-00AA006C42C4}][HKEY_CLASSES_ROOT\CLSID\<GUID>\Implemented Categories\{7DD95802-9882-11CF-9FA9-00AA006C42C4}] 我之前曾提到,通過(guò) SendFile 方法訪問(wèn)和發(fā)送用戶文件不是好的做法。實(shí)際上,如果我能夠訪問(wèn) SendFile 方法,并可以基于該方法返回的錯(cuò)誤代碼來(lái)確定用戶硬盤(pán)驅(qū)動(dòng)器中是否存在文件,那么它就是一個(gè)隱私錯(cuò)誤。 小結(jié) 這是我檢查代碼時(shí)通過(guò)的第一個(gè)非常高級(jí)別的審查。這些錯(cuò)誤中的大多數(shù)都非常簡(jiǎn)單,有人可能會(huì)爭(zhēng)辯說(shuō)開(kāi)發(fā)人員不應(yīng)該犯這樣的錯(cuò)誤,但他們確實(shí)會(huì)犯這樣的錯(cuò)誤。然而,了解到有人會(huì)對(duì)您代碼的進(jìn)行安全性檢查這一點(diǎn),通常會(huì)使您將編寫(xiě)更為安全的代碼放在第一位。 您可能還注意到,在包含某些缺陷類型的常見(jiàn)問(wèn)題中,多數(shù)是由不受信任的輸入造成的。當(dāng)在檢查代碼時(shí),您應(yīng)該始終詢問(wèn)數(shù)據(jù)從何而來(lái)、是否值得信任。 網(wǎng)絡(luò)的神奇作用吸引著越來(lái)越多的用戶加入其中,正因如此,網(wǎng)絡(luò)的承受能力也面臨著越來(lái)越嚴(yán)峻的考驗(yàn)―從硬件上、軟件上、所用標(biāo)準(zhǔn)上......,各項(xiàng)技術(shù)都需要適時(shí)應(yīng)勢(shì),對(duì)應(yīng)發(fā)展,這正是網(wǎng)絡(luò)迅速走向進(jìn)步的催化劑。 |
溫馨提示:喜歡本站的話,請(qǐng)收藏一下本站!