• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            woaidongmao

            文章均收錄自他人博客,但不喜標(biāo)題前加-[轉(zhuǎn)貼],因其丑陋,見諒!~
            隨筆 - 1469, 文章 - 0, 評(píng)論 - 661, 引用 - 0
            數(shù)據(jù)加載中……

            使您的軟件運(yùn)行起來(lái): 防止緩沖區(qū)溢出

            http://www.ibm.com/developerworks/cn/security/buffer-defend/index.html#resources

            上一篇專欄文章中,描述了高水平的緩沖區(qū)溢出攻擊,以及討論了為什么緩沖區(qū)溢出是如此嚴(yán)重的安全性問(wèn)題。本專欄文章的主題是,通過(guò)防御性編程保護(hù)代碼不受緩沖區(qū)溢出攻擊。我們將論及 C 編程語(yǔ)言中的主要安全性陷阱,顯示應(yīng)該避免特殊構(gòu)造的原因,以及演示推薦的編程實(shí)踐。最后,將討論有助于有效防止緩沖區(qū)溢出的其它技術(shù)。

            C 中大多數(shù)緩沖區(qū)溢出問(wèn)題可以直接追溯到標(biāo)準(zhǔn) C 庫(kù)。最有害的罪魁禍?zhǔn)资遣贿M(jìn)行自變量檢查的、有問(wèn)題的字符串操作(strcpy、strcat、sprintf 和 gets)。一般來(lái)講,象“避免使用 strcpy()”和“永遠(yuǎn)不使用 gets()”這樣嚴(yán)格的規(guī)則接近于這個(gè)要求。

            今天,編寫的程序仍然利用這些調(diào)用,因?yàn)閺膩?lái)沒(méi)有人教開發(fā)人員避免使用它們。某些人從各處獲得某個(gè)提示,但即使是優(yōu)秀的開發(fā)人員也會(huì)被這弄糟。他們也許在危險(xiǎn)函數(shù)的自變量上使用自己總結(jié)編寫的檢查,或者錯(cuò)誤地推論出使用潛在危險(xiǎn)的函數(shù)在某些特殊情況下是“安全”的。

            第一位公共敵人是 gets()。永遠(yuǎn)不要使用 gets()。該函數(shù)從標(biāo)準(zhǔn)輸入讀入用戶輸入的一行文本,它在遇到 EOF 字符或換行字符之前,不會(huì)停止讀入文本。也就是:gets() 根本不執(zhí)行邊界檢查。因此,使用 gets() 總是有可能使任何緩沖區(qū)溢出。作為一個(gè)替代方法,可以使用方法 fgets()。它可以做與 gets() 所做的同樣的事情,但它接受用來(lái)限制讀入字符數(shù)目的大小參數(shù),因此,提供了一種防止緩沖區(qū)溢出的方法。例如,不要使用以下代碼:

            void main()
              {
              char buf[1024];
              gets(buf);
              }
            

            而使用以下代碼:

            #define BUFSIZE 1024
            
            void main()
              {
              char buf[BUFSIZE];
              fgets(buf, BUFSIZE, stdin);
              }
            

            C 編程中的主要陷阱

            C 語(yǔ)言中一些標(biāo)準(zhǔn)函數(shù)很有可能使您陷入困境。但不是所有函數(shù)使用都不好。通常,利用這些函數(shù)之一需要任意輸入傳遞給該函數(shù)。這個(gè)列表包括:

            • strcpy()
            • strcat()
            • sprintf()
            • scanf()
            • sscanf()
            • fscanf()
            • vfscanf()
            • vsprintf
            • vscanf()
            • vsscanf()
            • streadd()
            • strecpy()
            • strtrns()

            壞消息是我們推薦,如果有任何可能,避免使用這些函數(shù)。好消息是,在大多數(shù)情況下,都有合理的替代方法。我們將仔細(xì)檢查它們中的每一個(gè),所以可以看到什么構(gòu)成了它們的誤用,以及如何避免它。

            strcpy()函數(shù)將源字符串復(fù)制到緩沖區(qū)。沒(méi)有指定要復(fù)制字符的具體數(shù)目。復(fù)制字符的數(shù)目直接取決于源字符串中的數(shù)目。如果源字符串碰巧來(lái)自用戶輸入,且沒(méi)有專門限制其大小,則有可能會(huì)陷入大的麻煩中!

            如果知道目的地緩沖區(qū)的大小,則可以添加明確的檢查:

            if(strlen(src) >= dst_size) {
              /* Do something appropriate, such as throw an error. */
              }
                   else {
              strcpy(dst, src);
            

            完成同樣目的的更容易方式是使用 strncpy() 庫(kù)例程:

            strncpy(dst, src, dst_size-1);
              dst[dst_size-1] = '\0'; /* Always do this to be safe! */
            

            如果 src 比 dst 大,則該函數(shù)不會(huì)拋出一個(gè)錯(cuò)誤;當(dāng)達(dá)到最大尺寸時(shí),它只是停止復(fù)制字符。注意上面調(diào)用 strncpy() 中的 -1。如果 src 比 dst 長(zhǎng),則那給我們留有空間,將一個(gè)空字符放在 dst 數(shù)組的末尾。

            當(dāng)然,可能使用 strcpy() 不會(huì)帶來(lái)任何潛在的安全性問(wèn)題,正如在以下示例中所見:

            strcpy(buf, "Hello!");
            

            即使這個(gè)操作造成 buf 的溢出,但它只是對(duì)幾個(gè)字符這樣而已。由于我們靜態(tài)地知道那些字符是什么,并且很明顯,由于沒(méi)有危害,所以這里無(wú)須擔(dān)心 ― 當(dāng)然,除非可以用其它方式覆蓋字符串“Hello”所在的靜態(tài)存儲(chǔ)器。

            確保 strcpy() 不會(huì)溢出的另一種方式是,在需要它時(shí)就分配空間,確保通過(guò)在源字符串上調(diào)用 strlen() 來(lái)分配足夠的空間。例如:

            dst = (char *)malloc(strlen(src));
              strcpy(dst, src);
            

            strcat()函數(shù)非常類似于 strcpy(),除了它可以將一個(gè)字符串合并到緩沖區(qū)末尾。它也有一個(gè)類似的、更安全的替代方法 strncat()。如果可能,使用 strncat() 而不要使用 strcat()。

            函數(shù) sprintf()vsprintf()是用來(lái)格式化文本和將其存入緩沖區(qū)的通用函數(shù)。它們可以用直接的方式模仿 strcpy() 行為。換句話說(shuō),使用 sprintf() 和 vsprintf() 與使用 strcpy() 一樣,都很容易對(duì)程序造成緩沖區(qū)溢出。例如,考慮以下代碼:

            void main(int argc, char **argv)
              {
              char usage[1024];
              sprintf(usage, "USAGE: %s -f flag [arg1]\n", argv[0]);
              }
            

            我們經(jīng)常會(huì)看到類似上面的代碼。它看起來(lái)沒(méi)有什么危害。它創(chuàng)建一個(gè)知道如何調(diào)用該程序字符串。那樣,可以更改二進(jìn)制的名稱,該程序的輸出將自動(dòng)反映這個(gè)更改。 雖然如此, 該代碼有嚴(yán)重的問(wèn)題。文件系統(tǒng)傾向于將任何文件的名稱限制于特定數(shù)目的字符。那么,您應(yīng)該認(rèn)為如果您的緩沖區(qū)足夠大,可以處理可能的最長(zhǎng)名稱,您的程序會(huì)安全,對(duì)嗎?只要將 1024 改為對(duì)我們的操作系統(tǒng)適合的任何數(shù)目,就好了嗎?但是,不是這樣的。通過(guò)編寫我們自己的小程序來(lái)推翻上面所說(shuō)的,可能容易地推翻這個(gè)限制:

            void main()
              {
              execl("/path/to/above/program", 
              <<insert really long string here>>, 
              NULL);
              }
            

            函數(shù) execl() 啟動(dòng)第一個(gè)參數(shù)中命名的程序。第二個(gè)參數(shù)作為 argv[0] 傳遞給被調(diào)用的程序。我們可以使那個(gè)字符串要多長(zhǎng)有多長(zhǎng)!

            那么如何解決 {v}sprintf() 帶來(lái)得問(wèn)題呢?遺憾的是,沒(méi)有完全可移植的方法。某些體系結(jié)構(gòu)提供了 snprintf() 方法,即允許程序員指定將多少字符從每個(gè)源復(fù)制到緩沖區(qū)中。例如,如果我們的系統(tǒng)上有 snprintf,則可以修正一個(gè)示例成為:

            void main(int argc, char **argv)
              {
              char usage[1024];
              char format_string = "USAGE: %s -f flag [arg1]\n";
              snprintf(usage, format_string, argv[0], 
              1024-strlen(format_string) + 1); 
              }
            

            注意,在第四個(gè)變量之前,snprintf() 與 sprintf() 是一樣的。第四個(gè)變量指定了從第三個(gè)變量中應(yīng)被復(fù)制到緩沖區(qū)的字符最大數(shù)目。注意,1024 是錯(cuò)誤的數(shù)目!我們必須確保要復(fù)制到緩沖區(qū)使用的字符串總長(zhǎng)不超過(guò)緩沖區(qū)的大小。所以,必須考慮一個(gè)空字符,加上所有格式字符串中的這些字符,再減去格式說(shuō)明符 %s。該數(shù)字結(jié)果為 1000, 但上面的代碼是更具有可維護(hù)性,因?yàn)槿绻袷阶址既话l(fā)生變化,它不會(huì)出錯(cuò)。

            {v}sprintf() 的許多(但不是全部)版本帶有使用這兩個(gè)函數(shù)的更安全的方法。可以指定格式字符串本身每個(gè)自變量的精度。例如,另一種修正上面有問(wèn)題的 sprintf() 的方法是:

            void main(int argc, char **argv)
              {
              char usage[1024];
              sprintf(usage, "USAGE: %.1000s -f flag [arg1]\n", argv[0]); 
              }
            

            注意,百分號(hào)后與 s 前的 .1000。該語(yǔ)法表明,從相關(guān)變量(本例中是 argv[0])復(fù)制的字符不超過(guò) 1000 個(gè)。

            如果任一解決方案在您的程序必須運(yùn)行的系統(tǒng)上行不通,則最佳的解決方案是將 snprintf() 的工作版本與您的代碼放置在一個(gè)包中。可以找到以 sh 歸檔格式的、自由使用的版本;請(qǐng)參閱 參考資料

            繼續(xù), scanf系列的函數(shù)也設(shè)計(jì)得很差。在這種情況下,目的地緩沖區(qū)會(huì)發(fā)生溢出。考慮以下代碼:

            void main(int argc, char **argv)
              {
              char buf[256];
              sscanf(argv[0], "%s", &buf);
              }
            

            如果輸入的字大于 buf 的大小,則有溢出的情況。幸運(yùn)的是,有一種簡(jiǎn)便的方法可以解決這個(gè)問(wèn)題。考慮以下代碼,它沒(méi)有安全性方面的薄弱環(huán)節(jié):

            void main(int argc, char **argv)
              {
              char buf[256];
              sscanf(argv[0], "%255s", &buf);
              }
            

            百分號(hào)和 s 之間的 255 指定了實(shí)際存儲(chǔ)在變量 buf 中來(lái)自 argv[0] 的字符不會(huì)超過(guò) 255 個(gè)。其余匹配的字符將不會(huì)被復(fù)制。

            接下來(lái),我們討論 streadd()strecpy()。由于,不是每臺(tái)機(jī)器開始就有這些調(diào)用,那些有這些函數(shù)的程序員,在使用它們時(shí),應(yīng)該小心。這些函數(shù)可以將那些含有不可讀字符的字符串轉(zhuǎn)換成可打印的表示。例如,考慮以下程序:

            #include <libgen.h>
            
            void main(int argc, char **argv)
              {
              char buf[20];
              streadd(buf, "\t\n", "");
              printf(%s\n", buf);
              }
            

            該程序打印:

            \t\n
            

            而不是打印所有空白。如果程序員沒(méi)有預(yù)料到需要多大的輸出緩沖區(qū)來(lái)處理輸入緩沖區(qū)(不發(fā)生緩沖區(qū)溢出),則 streadd() 和 strecpy() 函數(shù)可能有問(wèn)題。如果輸入緩沖區(qū)包含單一字符 ― 假設(shè)是 ASCII 001(control-A)― 則它將打印成四個(gè)字符“\001”。這是字符串增長(zhǎng)的最壞情況。如果沒(méi)有分配足夠的空間,以至于輸出緩沖區(qū)的大小總是輸入緩沖區(qū)大小的四倍,則可能發(fā)生緩沖區(qū)溢出。

            另一個(gè)較少使用的函數(shù)是 strtrns(),因?yàn)樵S多機(jī)器上沒(méi)有該函數(shù)。函數(shù) strtrns() 取三個(gè)字符串和結(jié)果字符串應(yīng)該放在其內(nèi)的一個(gè)緩沖區(qū),作為其自變量。第一個(gè)字符串必須復(fù)制到該緩沖區(qū)。一個(gè)字符被從第一個(gè)字符串中復(fù)制到緩沖區(qū),除非那個(gè)字符出現(xiàn)在第二個(gè)字符串中。如果出現(xiàn)的話,那么會(huì)替換掉第三個(gè)字符串中同一索引中的字符。這聽上去有點(diǎn)令人迷惑。讓我們看一下,將所有小寫字符轉(zhuǎn)換成大寫字符的示例:

            #include <libgen.h>
            
            void main(int argc, char **argv)
              {
              char lower[] = "abcdefghijklmnopqrstuvwxyz";
              char upper[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
              char *buf;
              if(argc < 2) {
              printf("USAGE: %s arg\n", argv[0]);
              exit(0);
              } buf = (char *)malloc(strlen(argv[1]));
              strtrns(argv[1], lower, upper, buf);
              printf("%s\n", buf);
              }
            

            以上代碼實(shí)際上不包含緩沖區(qū)溢出。但如果我們使用了固定大小的靜態(tài)緩沖區(qū),而不是用 malloc() 分配足夠空間來(lái)復(fù)制 argv[1],則可能會(huì)引起緩沖區(qū)溢出情況。





            回頁(yè)首


            避免內(nèi)部緩沖區(qū)溢出

            realpath() 函數(shù)接受可能包含相對(duì)路徑的字符串,并將它轉(zhuǎn)換成指同一文件的字符串,但是通過(guò)絕對(duì)路徑。在做這件事時(shí),它展開了所有符號(hào)鏈接。

            該函數(shù)取兩個(gè)自變量,第一個(gè)作為要規(guī)范化的字符串,第二個(gè)作為將存儲(chǔ)結(jié)果的緩沖區(qū)。當(dāng)然,需要確保結(jié)果緩沖區(qū)足夠大,以處理任何大小的路徑。分配的 MAXPATHLEN 緩沖區(qū)應(yīng)該足夠大。然而,使用 realpath() 有另一個(gè)問(wèn)題。如果傳遞給它的、要規(guī)范化的路徑大小大于 MAXPATHLEN,則 realpath() 實(shí)現(xiàn)內(nèi)部的靜態(tài)緩沖區(qū)會(huì)溢出!雖然實(shí)際上沒(méi)有訪問(wèn)溢出的緩沖區(qū),但無(wú)論如何它會(huì)傷害您的。結(jié)果是,應(yīng)該明確不使用 realpath(),除非確保檢查您試圖規(guī)范化的路徑長(zhǎng)度不超過(guò) MAXPATHLEN。

            其它廣泛可用的調(diào)用也有類似的問(wèn)題。經(jīng)常使用的 syslog() 調(diào)用也有類似的問(wèn)題,直到不久前,才注意到這個(gè)問(wèn)題并修正了它。大多數(shù)機(jī)器上已經(jīng)糾正了這個(gè)問(wèn)題,但您不應(yīng)該依賴正確的行為。最好總是假定代碼正運(yùn)行在可能最不友好的環(huán)境中,只是萬(wàn)一在哪天它真的這樣。getopt() 系列調(diào)用的各種實(shí)現(xiàn),以及 getpass() 函數(shù),都可能產(chǎn)生內(nèi)部靜態(tài)緩沖區(qū)溢出問(wèn)題。如果您不得不使用這些函數(shù),最佳解決方案是設(shè)置傳遞給這些函數(shù)的輸入長(zhǎng)度的閾值。

            自己模擬 gets() 的安全性問(wèn)題以及所有問(wèn)題是非常容易的。 例如,下面這段代碼:

            char buf[1024];
              int i = 0;
              char ch;
              while((ch = getchar()) != '\n')
              {
              if(ch == -1) break;
              buf[i++] = ch;
              }
            

            哎呀!可以用來(lái)讀入字符的任何函數(shù)都存在這個(gè)問(wèn)題,包括 getchar()、fgetc()、getc() 和 read()。

            緩沖區(qū)溢出問(wèn)題的準(zhǔn)則是:總是確保做邊界檢查。

            C 和 C++ 不能夠自動(dòng)地做邊界檢查,這實(shí)在不好,但確實(shí)有很好的原因,來(lái)解釋不這樣做的理由。邊界檢查的代價(jià)是效率。一般來(lái)講,C 在大多數(shù)情況下注重效率。然而,獲得效率的代價(jià)是,C 程序員必須十分警覺,并且有極強(qiáng)的安全意識(shí),才能防止他們的程序出現(xiàn)問(wèn)題,而且即使這些,使代碼不出問(wèn)題也不容易。

            在現(xiàn)在,變量檢查不會(huì)嚴(yán)重影響程序的效率。大多數(shù)應(yīng)用程序不會(huì)注意到這點(diǎn)差異。所以,應(yīng)該總是進(jìn)行邊界檢查。在將數(shù)據(jù)復(fù)制到您自己的緩沖區(qū)之前,檢查數(shù)據(jù)長(zhǎng)度。同樣,檢查以確保不要將過(guò)大的數(shù)據(jù)傳遞給另一個(gè)庫(kù),因?yàn)槟膊荒芟嘈牌渌说拇a!(回憶一下前面所討論的內(nèi)部緩沖區(qū)溢出。)





            回頁(yè)首


            其它危險(xiǎn)是什么?

            遺憾的是,即使是系統(tǒng)調(diào)用的“安全”版本 ― 譬如,相對(duì)于 strcpy() 的 strncpy() ― 也不完全安全。也有可能把事情搞糟。即使“安全”的調(diào)用有時(shí)會(huì)留下未終止的字符串,或者會(huì)發(fā)生微妙的相差一位錯(cuò)誤。當(dāng)然,如果您偶然使用比源緩沖區(qū)小的結(jié)果緩沖區(qū),則您可能發(fā)現(xiàn)自己處于非常困難的境地。

            與我們目前所討論的相比,往往很難犯這些錯(cuò)誤,但您應(yīng)該仍然意識(shí)到它們。當(dāng)使用這類調(diào)用時(shí),要仔細(xì)考慮。如果不仔細(xì)留意緩沖區(qū)大小,包括 bcopy()、fgets()、memcpy()、snprintf()、strccpy()、strcadd()、strncpy() 和 vsnprintf(),許多函數(shù)會(huì)行為失常。

            另一個(gè)要避免的系統(tǒng)調(diào)用是 getenv()。使用 getenv() 的最大問(wèn)題是您從來(lái)不能假定特殊環(huán)境變量是任何特定長(zhǎng)度的。我們將在后續(xù)的專欄文章中討論環(huán)境變量帶來(lái)的種種問(wèn)題。

            到目前為止,我們已經(jīng)給出了一大堆常見 C 函數(shù),這些函數(shù)容易引起緩沖區(qū)溢出問(wèn)題。當(dāng)然,還有許多函數(shù)有相同的問(wèn)題。特別是,注意第三方 COTS 軟件。不要設(shè)想關(guān)于其他人軟件行為的任何事情。還要意識(shí)到我們沒(méi)有仔細(xì)檢查每個(gè)平臺(tái)上的每個(gè)常見庫(kù)(我們不想做那一工作),并且還可能存在其它有問(wèn)題的調(diào)用。

            即使我們檢查了每個(gè)常見庫(kù)的各個(gè)地方,如果我們?cè)噲D聲稱已經(jīng)列出了將在任何時(shí)候遇到的所有問(wèn)題,則您應(yīng)該持非常非常懷疑的態(tài)度。我們只是想給您起一個(gè)頭。其余全靠您了。





            回頁(yè)首


            靜態(tài)和動(dòng)態(tài)測(cè)試工具

            我們將在以后的專欄文章中更加詳細(xì)地介紹一些脆弱性檢測(cè)的工具,但現(xiàn)在值得一提的是兩種已被證明能有效幫助找到和去除緩沖區(qū)溢出問(wèn)題的掃描工具。 這兩個(gè)主要類別的分析工具是靜態(tài)工具(考慮代碼但永不運(yùn)行)和動(dòng)態(tài)工具(執(zhí)行代碼以確定行為)。

            可以使用一些靜態(tài)工具來(lái)查找潛在的緩沖區(qū)溢出問(wèn)題。很糟糕的是,沒(méi)有一個(gè)工具對(duì)一般公眾是可用的!許多工具做得一點(diǎn)也不比自動(dòng)化 grep 命令多,可以運(yùn)行它以找到源代碼中每個(gè)有問(wèn)題函數(shù)的實(shí)例。由于存在更好的技術(shù),這仍然是高效的方式將幾萬(wàn)行或幾十萬(wàn)行的大程序縮減到只有數(shù)百個(gè)“潛在的問(wèn)題”。(在以后的專欄文章中,將演示一個(gè)基于這種方法的、草草了事的掃描工具,并告訴您有關(guān)如何構(gòu)建它的想法。)

            較好的靜態(tài)工具利用以某些方式表示的數(shù)據(jù)流信息來(lái)斷定哪個(gè)變量會(huì)影響到其它哪個(gè)變量。用這種方法,可以丟棄來(lái)自基于 grep 的分析的某些“假肯定”。David Wagner 在他的工作中已經(jīng)實(shí)現(xiàn)了這樣的方法(在“Learning the basics of buffer overflows”中描述;請(qǐng)參閱 參考資料),在 Reliable Software Technologies 的研究人員也已實(shí)現(xiàn)。當(dāng)前,數(shù)據(jù)流相關(guān)方法的問(wèn)題是它當(dāng)前引入了假否定(即,它沒(méi)有標(biāo)志可能是真正問(wèn)題的某些調(diào)用)。

            第二類方法涉及動(dòng)態(tài)分析的使用。動(dòng)態(tài)工具通常把注意力放在代碼運(yùn)行時(shí)的情況,查找潛在的問(wèn)題。一種已在實(shí)驗(yàn)室使用的方法是故障注入。這個(gè)想法是以這樣一種方式來(lái)檢測(cè)程序:對(duì)它進(jìn)行實(shí)驗(yàn),運(yùn)行“假設(shè)”游戲,看它會(huì)發(fā)生什么。有一種故障注入工具 ― FIST(請(qǐng)參閱 參考資料)已被用來(lái)查找可能的緩沖區(qū)溢出脆弱性。

            最終,動(dòng)態(tài)和靜態(tài)方法的某些組合將會(huì)給您的投資帶來(lái)回報(bào)。但在確定最佳組合方面,仍然有許多工作要做。





            回頁(yè)首


            Java 和堆棧保護(hù)可以提供幫助

            如上一篇專欄文章中所提到的(請(qǐng)參閱 參考資料),堆棧搗毀是最惡劣的一種緩沖區(qū)溢出攻擊,特別是,當(dāng)在特權(quán)模式下?lián)v毀了堆棧。這種問(wèn)題的優(yōu)秀解決方案是非可執(zhí)行堆棧。 通常,利用代碼是在程序堆棧上編寫,并在那里執(zhí)行的。(我們將在下一篇專欄文章中解釋這是如何做到的。)獲取許多操作系統(tǒng)(包括 Linux 和 Solaris)的非可執(zhí)行堆棧補(bǔ)丁是可能的。(某些操作系統(tǒng)甚至不需要這樣的補(bǔ)丁;它們本身就帶有。)

            非可執(zhí)行堆棧涉及到一些性能問(wèn)題。(沒(méi)有免費(fèi)的午餐。)此外,在既有堆棧溢出又有堆溢出的程序中,它們易出問(wèn)題。可以利用堆棧溢出使程序跳轉(zhuǎn)至利用代碼,該代碼被放置在堆上。 沒(méi)有實(shí)際執(zhí)行堆棧中的代碼,只有堆中的代碼。這些基本問(wèn)題非常重要,我們將在下一篇專欄文章中專門刊載。

            當(dāng)然,另一種選項(xiàng)是使用類型安全的語(yǔ)言,譬如 Java。較溫和的措施是獲取對(duì) C 程序中進(jìn)行數(shù)組邊界檢查的編譯器。對(duì)于 gcc 存在這樣的工具。這種技術(shù)可以防止所有緩沖區(qū)溢出,堆和堆棧。不利的一面是,對(duì)于那些大量使用指針、速度是至關(guān)重要的程序,這種技術(shù)可能會(huì)影響性能。但是在大多數(shù)情況下,該技術(shù)運(yùn)行得非常好。

            Stackguard 工具實(shí)現(xiàn)了比一般性邊界檢查更為有效的技術(shù)。它將一些數(shù)據(jù)放在已分配數(shù)據(jù)堆棧的末尾,并且以后會(huì)在緩沖區(qū)溢出可能發(fā)生前,查看這些數(shù)據(jù)是否仍然在那里。這種模式被稱之為“金絲雀”。(威爾士的礦工將 金絲雀放在礦井內(nèi)來(lái)顯示危險(xiǎn)的狀況。當(dāng)空氣開始變得有毒時(shí),金絲雀會(huì)昏倒,使礦工有足夠時(shí)間注意到并逃離。)

            Stackguard 方法不如一般性邊界檢查安全,但仍然相當(dāng)有用。Stackguard 的主要缺點(diǎn)是,與一般性邊界檢查相比,它不能防止堆溢出攻擊。一般來(lái)講,最好用這樣一個(gè)工具來(lái)保護(hù)整個(gè)操作系統(tǒng),否則,由程序調(diào)用的不受保護(hù)庫(kù)(譬如,標(biāo)準(zhǔn)庫(kù))可以仍然為基于堆棧的利用代碼攻擊打開了大門。

            類似于 Stackguard 的工具是內(nèi)存完整性檢查軟件包,譬如,Rational 的 Purify。這類工具甚至可以保護(hù)程序防止堆溢出,但由于性能開銷,這些工具一般不在產(chǎn)品代碼中使用。





            回頁(yè)首


            結(jié)束語(yǔ)

            在本專欄的上兩篇文章中,我們已經(jīng)介紹了緩沖區(qū)溢出,并指導(dǎo)您如何編寫代碼來(lái)避免這些問(wèn)題。我們還討論了可幫助使您的程序安全遠(yuǎn)離可怕的緩沖區(qū)溢出的幾個(gè)工具。表 1 總結(jié)了一些編程構(gòu)造,我們建議您小心使用或避免一起使用它們。如果有任何認(rèn)為我們應(yīng)該將其它函數(shù)加入該列表,請(qǐng)則通知我們,我們將更新該列表。

            函數(shù) 嚴(yán)重性 解決方案
            gets 最危險(xiǎn) 使用 fgets(buf, size, stdin)。這幾乎總是一個(gè)大問(wèn)題!
            strcpy 很危險(xiǎn) 改為使用 strncpy。
            strcat 很危險(xiǎn) 改為使用 strncat。
            sprintf 很危險(xiǎn) 改為使用 snprintf,或者使用精度說(shuō)明符。
            scanf 很危險(xiǎn) 使用精度說(shuō)明符,或自己進(jìn)行解析。
            sscanf 很危險(xiǎn) 使用精度說(shuō)明符,或自己進(jìn)行解析。
            fscanf 很危險(xiǎn) 使用精度說(shuō)明符,或自己進(jìn)行解析。
            vfscanf 很危險(xiǎn) 使用精度說(shuō)明符,或自己進(jìn)行解析。
            vsprintf 很危險(xiǎn) 改為使用 vsnprintf,或者使用精度說(shuō)明符。
            vscanf 很危險(xiǎn) 使用精度說(shuō)明符,或自己進(jìn)行解析。
            vsscanf 很危險(xiǎn) 使用精度說(shuō)明符,或自己進(jìn)行解析。
            streadd 很危險(xiǎn) 確保分配的目的地參數(shù)大小是源參數(shù)大小的四倍。
            strecpy 很危險(xiǎn) 確保分配的目的地參數(shù)大小是源參數(shù)大小的四倍。
            strtrns 危險(xiǎn) 手工檢查來(lái)查看目的地大小是否至少與源字符串相等。
            realpath 很危險(xiǎn)(或稍小,取決于實(shí)現(xiàn)) 分配緩沖區(qū)大小為 MAXPATHLEN。同樣,手工檢查參數(shù)以確保輸入?yún)?shù)不超過(guò) MAXPATHLEN。
            syslog 很危險(xiǎn)(或稍小,取決于實(shí)現(xiàn)) 在將字符串輸入傳遞給該函數(shù)之前,將所有字符串輸入截成合理的大小。
            getopt 很危險(xiǎn)(或稍小,取決于實(shí)現(xiàn)) 在將字符串輸入傳遞給該函數(shù)之前,將所有字符串輸入截成合理的大小。
            getopt_long 很危險(xiǎn)(或稍小,取決于實(shí)現(xiàn)) 在將字符串輸入傳遞給該函數(shù)之前,將所有字符串輸入截成合理的大小。
            getpass 很危險(xiǎn)(或稍小,取決于實(shí)現(xiàn)) 在將字符串輸入傳遞給該函數(shù)之前,將所有字符串輸入截成合理的大小。
            getchar 中等危險(xiǎn) 如果在循環(huán)中使用該函數(shù),確保檢查緩沖區(qū)邊界。
            fgetc 中等危險(xiǎn) 如果在循環(huán)中使用該函數(shù),確保檢查緩沖區(qū)邊界。
            getc 中等危險(xiǎn) 如果在循環(huán)中使用該函數(shù),確保檢查緩沖區(qū)邊界。
            read 中等危險(xiǎn) 如果在循環(huán)中使用該函數(shù),確保檢查緩沖區(qū)邊界。
            bcopy 低危險(xiǎn) 確保緩沖區(qū)大小與它所說(shuō)的一樣大。
            fgets 低危險(xiǎn) 確保緩沖區(qū)大小與它所說(shuō)的一樣大。
            memcpy 低危險(xiǎn) 確保緩沖區(qū)大小與它所說(shuō)的一樣大。
            snprintf 低危險(xiǎn) 確保緩沖區(qū)大小與它所說(shuō)的一樣大。
            strccpy 低危險(xiǎn) 確保緩沖區(qū)大小與它所說(shuō)的一樣大。
            strcadd 低危險(xiǎn) 確保緩沖區(qū)大小與它所說(shuō)的一樣大。
            strncpy 低危險(xiǎn) 確保緩沖區(qū)大小與它所說(shuō)的一樣大。
            vsnprintf 低危險(xiǎn) 確保緩沖區(qū)大小與它所說(shuō)的一樣大。

            在我們急匆匆講述這些基礎(chǔ)知識(shí)時(shí),到現(xiàn)在為止,已經(jīng)遺漏了一些緩沖區(qū)溢出很酷的細(xì)節(jié)。在下幾篇專欄文章中,我們將深入這臺(tái)“引擎”的工作,并給它加點(diǎn)黃油。我們將詳細(xì)地了解緩沖區(qū)溢出的工作原理,甚至還會(huì)演示一些利用代碼。



            參考資料



            作者簡(jiǎn)介

            Gary McGraw是 Reliable Software Technologies 負(fù)責(zé)企業(yè)技術(shù)的副總裁,該公司位于美國(guó)弗吉尼亞州杜勒斯(Dulles)。他從事咨詢服務(wù)和研究工作,幫助決定技術(shù)研究和開發(fā)方向。McGraw 在 Reliable Software Technologies 從一個(gè)研究科學(xué)家做起,從事軟件工程和計(jì)算機(jī)安全性方面的研究。他擁有印第安那大學(xué)認(rèn)知科學(xué)和計(jì)算機(jī)科學(xué)雙博士學(xué)位,弗吉尼亞大學(xué)的哲學(xué)學(xué)士學(xué)位。他為技術(shù)刊物撰寫了 40 余篇經(jīng)同行審查的文章,擔(dān)任過(guò)主要的電子貿(mào)易供應(yīng)商(包括 Visa 和 Federal Reserve)的顧問(wèn)職務(wù),并在空軍研究實(shí)驗(yàn)室、DARPA、國(guó)家科學(xué)基金會(huì)以及 NIST 的高級(jí)技術(shù)項(xiàng)目贊助下?lián)纹涫紫{(diào)研員。

            McGraw 是移動(dòng)代碼安全性方面著名的權(quán)威人士,并且與普林斯頓的教授 Ed Felten 合作撰寫了“Java Security: Hostile Applets, Holes, & Antidotes”(Wiley, 1996)以及“Securing Java: Getting down to business with mobile code”(Wiley, 1999)。McGraw 和 RST 創(chuàng)始人之一、首席科學(xué)家 Dr. Jeffrey Voas 一起編寫了“Software Fault Injection: Inoculating Programs Against Errors”(Wiley, 1998)。McGraw 定期為一些受歡迎的商業(yè)出版物撰稿,而且其文章經(jīng)常在全國(guó)出版的文章中所引用。


            John Viega是一名高級(jí)副研究員,Software Security Group 的共同創(chuàng)始人,并擔(dān)任 Reliable Software Technologies 的高級(jí)顧問(wèn)。他是 DARPA 贊助的開發(fā)標(biāo)準(zhǔn)編程語(yǔ)言安全性擴(kuò)展的首席調(diào)研員。John 已撰寫了 30 余篇涉及軟件安全性和測(cè)試領(lǐng)域的技術(shù)性文章。他負(fù)責(zé)在主要網(wǎng)絡(luò)和電子商業(yè)產(chǎn)品中查找一些眾所周知的安全性弱點(diǎn),包括最近在 Netscape 安全性中的缺陷。他還是開放源碼軟件社區(qū)的重要成員,編寫過(guò) Mailman、GNU Mailing List Manager 以及最近發(fā)布的 ITS4(一種在 C 和 C++ 代碼中查找安全性弱點(diǎn)的工具)。Viega 擁有弗吉尼亞大學(xué)計(jì)算機(jī)科學(xué)碩士學(xué)位。

            posted on 2008-05-10 20:56 肥仔 閱讀(916) 評(píng)論(0)  編輯 收藏 引用 所屬分類: C++ 基礎(chǔ)

            爱做久久久久久| 欧美性大战久久久久久| 久久人人爽人人爽人人片av高请| 亚洲婷婷国产精品电影人久久| 怡红院日本一道日本久久 | 精品蜜臀久久久久99网站| 亚洲AV无码成人网站久久精品大| 久久亚洲AV成人出白浆无码国产| 欧美午夜精品久久久久免费视| 国内精品久久久久影院优| 情人伊人久久综合亚洲| 久久久免费观成人影院| 中文字幕无码av激情不卡久久| 五月丁香综合激情六月久久| 国产精品美女久久久久网| 四虎国产精品免费久久久| 性做久久久久久久久老女人| 伊人久久大香线蕉亚洲五月天| 2022年国产精品久久久久| 久久国产成人午夜aⅴ影院 | 亚洲乱码中文字幕久久孕妇黑人 | 久久久久女教师免费一区| 久久久久亚洲AV成人网人人网站| 日韩AV无码久久一区二区 | 久久久国产精华液| 久久久精品人妻一区二区三区四 | 老色鬼久久亚洲AV综合| 亚洲欧美精品伊人久久| 精品久久久久成人码免费动漫| 亚洲成色www久久网站夜月| 免费精品99久久国产综合精品| 亚洲精品成人网久久久久久| 久久无码人妻一区二区三区| 久久精品成人一区二区三区| 久久久噜噜噜久久熟女AA片| 久久久久久亚洲精品不卡| 久久国产精品99国产精| 理论片午午伦夜理片久久| 久久91精品国产91久久户| 久久久SS麻豆欧美国产日韩| 国产农村妇女毛片精品久久|