自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

關(guān)于模糊測(cè)試(Fuzz Testing)

開發(fā) 開發(fā)工具
在本文中, 故意將隨機(jī)的壞數(shù)據(jù)插入應(yīng)用程序,以觀察發(fā)生的結(jié)果。同時(shí)也解釋了如何使用如校驗(yàn)和、XML 數(shù)據(jù)存儲(chǔ)及代碼驗(yàn)證等防護(hù)性編碼技術(shù),來加固您的程序以抵制隨機(jī)數(shù)據(jù)。

模糊測(cè)試(Fuzz Testing)

模糊測(cè)試(Fuzz testing )是一項(xiàng)對(duì)代碼質(zhì)量有著深遠(yuǎn)影響的簡(jiǎn)單技術(shù)。在本文中,Elliotte Rusty Harold 故意將隨機(jī)的壞數(shù)據(jù)插入應(yīng)用程序,以觀察發(fā)生的結(jié)果。他也解釋了如何使用如校驗(yàn)和、XML 數(shù)據(jù)存儲(chǔ)及代碼驗(yàn)證等防護(hù)性編碼技術(shù),來加固您的程序以抵制隨機(jī)數(shù)據(jù)。他以一個(gè)練習(xí)進(jìn)行總結(jié),在練習(xí)中他以一個(gè)代碼破壞者的角度進(jìn)行思考 —— 這是一種用于防護(hù)代碼的至關(guān)重要的技術(shù)

多年來,我驚嘆于有如此大量能夠使 Microsoft Word 崩潰的壞文件。少數(shù)字節(jié)錯(cuò)位,會(huì)使整個(gè)應(yīng)用程序毀于一旦。在舊式的、無內(nèi)存保護(hù)的操作系統(tǒng)中,整個(gè)計(jì)算機(jī)通常就這樣宕掉了。Word 為什么不能意識(shí)到它接收到了壞的數(shù)據(jù),并發(fā)出一條錯(cuò)誤信息呢?為什么它會(huì)僅僅因?yàn)樯贁?shù)字節(jié)被損壞就破壞自己的棧、堆呢?當(dāng)然,Word 并不是惟一一個(gè)面對(duì)畸形文件時(shí)表現(xiàn)得如此糟糕的程序。

本文介紹了一種試圖避免這種災(zāi)難的技術(shù)。在模糊測(cè)試中,用隨機(jī)壞數(shù)據(jù)(也稱做 fuzz)攻擊一個(gè)程序,然后等著觀察哪里遭到了破壞。模糊測(cè)試的技巧在于,它是不符合邏輯的:自動(dòng)模糊測(cè)試不去猜測(cè)哪個(gè)數(shù)據(jù)會(huì)導(dǎo)致破壞(就像人工測(cè)試員那樣),而是將盡可能多的雜亂數(shù)據(jù)投入程序中。由這個(gè)測(cè)試驗(yàn)證過的失敗模式通常對(duì)程序員來說是個(gè)徹底的震憾,因?yàn)槿魏伟催壿嬎伎嫉娜硕疾粫?huì)想到這種失敗。

模糊測(cè)試是一項(xiàng)簡(jiǎn)單的技術(shù),但它卻能揭示出程序中的重要 bug。它能夠驗(yàn)證出現(xiàn)實(shí)世界中的錯(cuò)誤模式并在您的軟件發(fā)貨前對(duì)潛在的應(yīng)當(dāng)被堵塞的攻擊渠道進(jìn)行提示。

模糊測(cè)試如何運(yùn)行

模糊測(cè)試的實(shí)現(xiàn)是一個(gè)非常簡(jiǎn)單的過程:

  1. 準(zhǔn)備一份插入程序中的正確的文件。
  2. 用隨機(jī)數(shù)據(jù)替換該文件的某些部分。
  3. 用程序打開文件。
  4. 觀察破壞了什么。

可以用任意多種方式改變?cè)撾S機(jī)數(shù)據(jù)。例如,可以將整個(gè)文件打亂,而不是僅替換其中的一部分,也可以將該文件限制為 ASCII 文本或非零字節(jié)。不管用什么方式進(jìn)行分割,關(guān)鍵是將大量隨機(jī)數(shù)據(jù)放入應(yīng)用程序并觀察出故障的是什么。

測(cè)試基于 C 的應(yīng)用程序:當(dāng)字符串包含額外的零時(shí),許多用 C 編寫的程序都會(huì)出問題 —— 這類問題太過頻繁以至于額外的零能夠徹底隱藏代碼中其他的問題。一旦驗(yàn)證出程序存在零字節(jié)問題,就可以移除它們,從而讓其他的問題浮現(xiàn)出來。

可以手動(dòng)進(jìn)行初始化測(cè)試,但要想達(dá)到最佳的效果則確實(shí)需要采用自動(dòng)化模糊測(cè)試。在這種情況下,當(dāng)面臨破壞輸入時(shí)首先需要為應(yīng)用程序定義適當(dāng)?shù)腻e(cuò)誤行為。(如果當(dāng)輸入數(shù)據(jù)被破壞時(shí),您發(fā)現(xiàn)程序正常運(yùn)行,且未定義發(fā)生的事件,那么這就是第一個(gè) bug。)隨后將隨機(jī)數(shù)據(jù)傳遞到程序中直到找到了一個(gè)文件,該文件不會(huì)觸發(fā)適當(dāng)?shù)腻e(cuò)誤對(duì)話框、消息、異常,等等。存儲(chǔ)并記錄該文件,這樣就能在稍后重現(xiàn)該問題。如此重復(fù)。

盡管模糊測(cè)試通常需要一些手動(dòng)編碼,但還有一些工具能提供幫助。例如,清單 1 顯示了一個(gè)簡(jiǎn)單的 Java™ 類,該類隨機(jī)更改文件的特定長(zhǎng)度。我常愿意在開始的幾個(gè)字節(jié)后面啟動(dòng)模糊測(cè)試,因?yàn)槌绦蛩坪醺赡茏⒁獾皆缙诘腻e(cuò)誤而不是后面的錯(cuò)誤。(您的目的是想找到程序未檢測(cè)到的錯(cuò)誤,而不是尋找已經(jīng)檢測(cè)到的。)

清單 1. 用隨機(jī)數(shù)據(jù)替換文件部分的類

  1. import java.io.*; 
  2. import java.security.SecureRandom; 
  3. import java.util.Random; 
  4.  
  5. public class Fuzzer { 
  6.  
  7.      private Random random = new SecureRandom(); 
  8.      private int count = 1
  9.  
  10.      public File fuzz(File in, int start, int length) throws IOException   
  11.  
  12.          byte[] data = new byte[(int) in.length()]; 
  13.          DataInputStream din = new DataInputStream(new FileInputStream(in)); 
  14.          din.readFully(data); 
  15.          fuzz(data, start, length); 
  16.          String name = "fuzz_" + count + "_" + in.getName(); 
  17.          File fout = new File(name); 
  18.          FileOutputStream  out = new FileOutputStream(fout); 
  19.          out.write(data); 
  20.          out.close(); 
  21.          din.close(); 
  22.          count++; 
  23.          return fout; 
  24.      } 
  25.  
  26.  
  27.      // Modifies byte array in place 
  28.      public void fuzz(byte[] in, int start, int length) { 
  29.  
  30.          byte[] fuzz = new byte[length]; 
  31.          random.nextBytes(fuzz); 
  32.          System.arraycopy(fuzz, 0, in, start, fuzz.length); 
  33.  
  34.      } 
  35.  

模糊測(cè)試文件很簡(jiǎn)單。將其傳至應(yīng)用程序通常不那么困難。如 AppleScript 或 Perl 腳本語言通常是編寫模糊測(cè)試的最佳選擇。對(duì)于 GUI 程序,最困難的部分是辨認(rèn)出應(yīng)用程序是否檢測(cè)出正確的故障模式。有時(shí),最簡(jiǎn)單的方法是讓一個(gè)人坐在程序前將每一個(gè)測(cè)試通過或失敗的結(jié)果都標(biāo)記下來。一定要將所有生成的隨機(jī)測(cè)試用例單獨(dú)地命名并保存下來,這樣就能夠重現(xiàn)這個(gè)過程中檢測(cè)到的任何故障。

防護(hù)性編碼

可靠的編碼遵循了這樣的基本原則:絕不會(huì)讓程序中插入未經(jīng)過一致性及合理性驗(yàn)證的外部數(shù)據(jù)。

如果從文件中讀入一個(gè)數(shù)字并期望其為正數(shù),那么,在使用其進(jìn)行進(jìn)一步處理前對(duì)其先驗(yàn)證一下。如果期望字符串只包含 ASCII 字母,請(qǐng)確定它確實(shí)是這樣。如果認(rèn)為文件包含一個(gè)四字節(jié)的整數(shù)倍的數(shù)據(jù),請(qǐng)驗(yàn)證一下。一定不要假設(shè)任何外部提供的數(shù)據(jù)中的字符都會(huì)如您所料。

最常見的錯(cuò)誤是做出這樣的假設(shè):因?yàn)槌绦驅(qū)⒃摂?shù)據(jù)寫出,該程序就能不用驗(yàn)證再一次將該數(shù)據(jù)讀回去。這是很危險(xiǎn)的!因?yàn)樵摂?shù)據(jù)很可能已經(jīng)被另一個(gè)程序在磁盤上復(fù)寫過了。它也可能已經(jīng)被一個(gè)故障磁盤或壞的網(wǎng)絡(luò)傳輸所破壞了或已經(jīng)被另一個(gè)帶 bug 的程序更改過了。它甚至可能已經(jīng)被故意更改過以破壞程序的安全性。所以不要假設(shè)任何事,要進(jìn)行驗(yàn)證。

當(dāng)然,錯(cuò)誤處理及驗(yàn)證十分令人生厭,也很不方便,并被全世界程序員們所輕視。計(jì)算機(jī)的誕生已進(jìn)入了六十個(gè)年頭,我們?nèi)耘f沒有檢查基本的東西,如成功打開一個(gè)文件及內(nèi)存分配是否成功。讓程序員們?cè)陂喿x一個(gè)文件時(shí)測(cè)試每一個(gè)字節(jié)和每一個(gè)不變量似乎是無望的 —— 但不這樣做就會(huì)使程序易被模糊攻擊。幸運(yùn)的是,可以尋求幫助。恰當(dāng)使用現(xiàn)代工具和技術(shù)能夠顯著減輕加固應(yīng)用程序的痛苦,特別是如下三種技術(shù)更為突出:

  • 校驗(yàn)和
  • 基于語法的格式,如 XML
  • 驗(yàn)證過的代碼如 Java

用校驗(yàn)和進(jìn)行的模糊試驗(yàn)

能夠保護(hù)程序抵御模糊攻擊的最簡(jiǎn)單的方法是將一個(gè)檢驗(yàn)和添加到數(shù)據(jù)中。例如,可以將文件中所有的字節(jié)都累加起來,然后取其除以 256 的余數(shù)。將得到的值存儲(chǔ)到文件尾部的一個(gè)額外字節(jié)中。然后,在輸入數(shù)據(jù)前,先驗(yàn)證檢驗(yàn)和是否匹配。這項(xiàng)簡(jiǎn)單模式將未被發(fā)現(xiàn)的意外故障的風(fēng)險(xiǎn)降低到約 1/256 。

健壯的校驗(yàn)和算法如 MD5 和 SHA 并不僅僅取其除以 256 的余數(shù),它完成的要多得多。在 Java 語言中,java.security.DigestInputStream 和 java.security.DigestOutputStream 類為將一個(gè)校驗(yàn)和附屬到數(shù)據(jù)中提供了便捷的方式。使用這些校驗(yàn)和算法中的一種可以將程序遭受意外破壞的機(jī)率降低到少于十億分之一(盡管故意攻擊仍有可能)。

XML 存儲(chǔ)及驗(yàn)證

將數(shù)據(jù)以 XML 形式存儲(chǔ)是一種避免數(shù)據(jù)損壞的好方法。XML 最初即著力于 Web 頁面、書籍、詩(shī)歌、文章及相似文檔,它幾乎在每個(gè)領(lǐng)域都獲取了巨大的成功,從金融數(shù)據(jù)到矢量圖形到序列化對(duì)象等等。

不切實(shí)際的限定

如果真想要破壞一個(gè) XML 解析器,有幾種方法可以試試。例如,大多數(shù) XML 解析器服從于特定的最大尺寸。如果一個(gè)元素名長(zhǎng)度超過 22 億字符(Java String 的最大尺寸),SAX 解析器將會(huì)失敗。盡管如此,在實(shí)踐中這些極限值如此之高,以至于在達(dá)到之前內(nèi)存就已經(jīng)耗盡。

使 XML 格式抵制模糊攻擊的關(guān)鍵特征是一個(gè)對(duì)輸入不做任何 假設(shè)的解析器。這就是真正想在一個(gè)健壯的文件格式中所獲得的。設(shè)計(jì) XML 解析器是為了讓任何輸入(格式良好的或無格式的,有效的或無效的)都以定義好的形式處理。XML 解析器能夠處理任何 字節(jié)流。如果數(shù)據(jù)首先通過了 XML 解析器,則僅需要準(zhǔn)備好接受解析器所能提供的東西。例如,不需要檢查數(shù)據(jù)是否包含空字符,因?yàn)?XML 解析器絕不會(huì)傳送一個(gè)空值。如果 XML 解析器在其輸入中看到一個(gè)空字符,它就會(huì)發(fā)出異常并停止處理。當(dāng)然還需要處理這個(gè)異常,但編寫一個(gè) catch 塊來處理檢測(cè)到的錯(cuò)誤比起編寫代碼來檢測(cè)所有可能的錯(cuò)誤來說要簡(jiǎn)單得多。

為使程序更加安全,可以用 DTD 和/或模式來驗(yàn)證文檔。這不僅檢查了 XML 是否格式良好,而且至少與所預(yù)期更加接近。驗(yàn)證并不會(huì)告知關(guān)于文檔所需了解的一切,但它卻使編寫大量簡(jiǎn)單檢查變得很簡(jiǎn)單。用 XML,很明顯能夠?qū)⑺邮艿奈臋n嚴(yán)格地限定為能夠處理的格式。

盡管如此,還有多段代碼不能用 DTD 或模式進(jìn)行驗(yàn)證。例如,不能測(cè)試發(fā)票上商品的價(jià)格是否和數(shù)據(jù)庫(kù)中庫(kù)存商品的價(jià)格一致。當(dāng)從客戶接收到一份包含價(jià)格的訂單文檔時(shí),不論其是 XML 格式或是其他格式,在提交前通常都會(huì)檢查一下,以確??蛻舨⑽葱薷膬r(jià)格??梢杂枚ㄖ拼a實(shí)現(xiàn)這些最后的檢查。

基于語法的格式

使 XML 能夠?qū)δ:艟哂腥绱说牡钟芰Φ氖瞧涫褂冒涂扑?諾爾范式(Backus-Naur Form,BNF)語法仔細(xì)且標(biāo)準(zhǔn)地定義的格式。許多解析器都是使用如 JavaCC 或 Bison 等解析器-生成器工具直接從此語法中構(gòu)建的。這種工具的實(shí)質(zhì)是閱讀一個(gè)任意的輸入流并確定其是否符合此語法。

如果 XML 并不適合于您的文件格式,您仍可以從基于解析器的解決方案的健壯性中獲益。您必須為文件格式自行編寫語法,隨后開發(fā)自己的解析器來閱讀它。相比使用唾手可得的 XML 解析器,開發(fā)自己的解析器需要更多的工作。然而它是一個(gè)更為健壯的解決方案,而不是不根據(jù)語法正式地進(jìn)行驗(yàn)證就將數(shù)據(jù)簡(jiǎn)單地裝載到內(nèi)存中。

Java 代碼驗(yàn)證

由模糊測(cè)試導(dǎo)致的許多故障都是內(nèi)存分配錯(cuò)誤及緩沖器溢出的結(jié)果。用一種安全的垃圾收集語言(在如 Java 或 managed C# 等虛擬機(jī)上執(zhí)行的)來編寫應(yīng)用程序避免了許多潛在問題。即使用 C 或 C++ 來編寫代碼,還是需要使用一個(gè)可靠的垃圾收集庫(kù)。在 2006 年,臺(tái)式機(jī)程序員或服務(wù)器程序員不應(yīng)該還需要管理內(nèi)存。

Java 運(yùn)行時(shí)對(duì)其自身的代碼起到了額外保護(hù)層的作用。在將一個(gè) .class 文件裝載到虛擬機(jī)之前,該文件要由一個(gè)字節(jié)符驗(yàn)證器或一個(gè)可選的 SecurityManager 進(jìn)行驗(yàn)證。Java 并不假設(shè)創(chuàng)建 .class 文件的編譯器沒有 bug 且運(yùn)轉(zhuǎn)正常。設(shè)計(jì) Java 語言之初就是為了允許在一個(gè)安全沙箱中運(yùn)行不信任的、潛在惡意的代碼。它甚至不信任其自身編譯過的代碼。畢竟,也許有人已經(jīng)用十六進(jìn)制編輯器手工修改了字節(jié)符,試圖觸發(fā)緩沖器溢出。我們大家都應(yīng)該對(duì)我們的程序也有對(duì)輸入這樣的偏執(zhí)。

以敵人的角度思考

之前介紹的每項(xiàng)技術(shù)都在阻止意外破壞方面造詣?lì)H深。將它們綜合起來恰當(dāng)?shù)貙?shí)現(xiàn),會(huì)將未被發(fā)現(xiàn)的非蓄意破壞發(fā)生的可能性幾乎減少到零。(當(dāng)然,并不會(huì)減少到零,但其發(fā)生的可能性就如同一束偏離軌道的宇宙射線將您 CPU 運(yùn)算 1+1 的結(jié)果變?yōu)?3 的可能性一樣微乎其微。)但不是所有的數(shù)據(jù)損壞都是非蓄意的。如果有人故意引入壞數(shù)據(jù)來破壞程序的安全性又該如何呢?以一個(gè)攻擊者的角度進(jìn)行思考是防護(hù)代碼的下一個(gè)步驟。

轉(zhuǎn)回到一個(gè)攻擊者的角度進(jìn)行思考,假設(shè)要攻擊的應(yīng)用程序是用 Java 編程語言編寫的、使用非本地代碼且將所有額外數(shù)據(jù)都以 XML(在接受前經(jīng)過徹底驗(yàn)證)形式存儲(chǔ),還能成功攻擊嗎?是的,能。但用隨機(jī)改變文件字節(jié)的低級(jí)方法顯然不行。需要一種更為復(fù)雜的方法來說明程序自身的錯(cuò)誤檢測(cè)機(jī)制及路徑。

當(dāng)測(cè)試一個(gè)抵御模糊攻擊的應(yīng)用程序時(shí),不可能做純黑盒測(cè)試,但通過一些明顯的修改,基本的想法還是可以應(yīng)用的。例如,考慮校驗(yàn)和,如果文件格式包含一個(gè)校驗(yàn)和,在將文件傳至應(yīng)用程序前僅僅修改此校驗(yàn)和就可以使其同隨機(jī)數(shù)據(jù)相匹配。

對(duì)于 XML,試著模糊單獨(dú)的元素內(nèi)容和屬性值,而不是從文檔中挑選一部分隨機(jī)的字節(jié)進(jìn)行替換。一定要用合法的 XML 字符替換數(shù)據(jù),而不要用隨機(jī)字節(jié),因?yàn)榧词挂话僮止?jié)的隨機(jī)數(shù)據(jù)也一定是畸形的。也可以改變?cè)孛Q和屬性名稱,只要細(xì)心地確保得到的文檔格式仍是正確的就可以了。如果該 XML 文檔是由一個(gè)限制非常嚴(yán)格的模式進(jìn)行檢查的,還需要計(jì)算出該模式?jīng)]有 檢查什么,以決定在哪里進(jìn)行有效的模糊。

一個(gè)結(jié)合了對(duì)剩余數(shù)據(jù)進(jìn)行代碼級(jí)驗(yàn)證的真正嚴(yán)格的模式也許不會(huì)留下可操縱的空間。這就是作為一個(gè)開發(fā)人員所需要追求的。應(yīng)用程序應(yīng)能夠處理所發(fā)送的任何有意義的字節(jié)流,而不會(huì)因無效而拒絕。

結(jié)束語

模糊測(cè)試能夠說明 bug 在程序中的出現(xiàn)。并不證明不存在這樣的 bug。而且,通過模糊測(cè)試會(huì)極大地提高您對(duì)應(yīng)用程序的健壯性及抵御意外輸入的安全性的自信心。如果您用 24 小時(shí)對(duì)程序進(jìn)行模糊測(cè)試而其依然無事,那么隨后同種類型的攻擊就不大可能再危及到它。(并不是不可能,提醒您,只是可能性很小。)如果模糊測(cè)試揭示出程序中的 bug,就應(yīng)該進(jìn)行修正,而不是當(dāng) bug 隨機(jī)出現(xiàn)時(shí)再對(duì)付它們。模糊測(cè)試通過明智地使用校驗(yàn)和、XML、垃圾收集和/或基于語法的文件格式,更有效地從根本上加固了文件格式。

模糊測(cè)試是一項(xiàng)用于驗(yàn)證程序中真實(shí)錯(cuò)誤的重要工具,也是所有意識(shí)到安全性問題且著力于程序健壯性的程序員們的工具箱中所必備的工具。

【本文是51CTO專欄作者elknot的原創(chuàng)文章,轉(zhuǎn)載請(qǐng)通過51CTO獲取授權(quán)】

戳這里,看該作者更多好文

責(zé)任編輯:趙寧寧 來源: 51CTO專欄
相關(guān)推薦

2023-08-01 09:27:44

Golang模糊測(cè)試

2016-12-09 08:55:29

2024-10-21 15:39:24

2021-06-08 11:19:43

GoBeta測(cè)試Fuzzing

2017-05-08 07:37:56

2010-09-17 15:12:28

2023-07-07 08:38:49

單元測(cè)試軟件開發(fā)

2018-12-11 10:55:00

SQLFuzzWEB安全

2012-02-22 14:18:06

測(cè)試測(cè)試人員

2009-09-14 18:06:18

LINQ模糊查詢

2020-03-19 14:50:31

Reac單元測(cè)試前端

2010-08-10 15:17:08

應(yīng)用安全網(wǎng)絡(luò)測(cè)試

2019-08-12 15:17:23

USB模糊測(cè)試

2023-07-04 00:10:47

2021-02-25 15:51:41

Go語言模糊測(cè)試功能

2016-12-05 15:48:37

2011-09-09 16:23:16

Android Web測(cè)試

2023-07-04 08:28:27

2020-03-12 11:17:18

模糊測(cè)試工具漏洞網(wǎng)絡(luò)安全
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)