• <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>

            C/C++禁用危險API

            Posted on 2008-11-13 11:51 Herbert 閱讀(3465) 評論(1)  編輯 收藏 引用 所屬分類: C++
             轉(zhuǎn)自:http://bbs.lvy8.cn/ShowPost_PR5339.html


            程序員》9月文章

             

            申明。文章僅代表個人觀點,與所在公司無任何聯(lián)系。

            1. 概述

               在前面的安全編碼實踐的文章里,我們討論了GS編譯選項,數(shù)據(jù)執(zhí)行保護DEP功能,以及靜態(tài)代碼分析工具Prefast這里,我們討論在C/C++代碼中禁用危險的API其主要目的是為了減少代碼中引入安全漏洞的可能性。

            1. 那些是危險的API

              2.1歷史

               在微軟產(chǎn)品的安全漏洞中,有很大一部分是由于不正確的使用C動態(tài)庫(C Runtime Library 的函數(shù),特別是有關字符串處理的函數(shù)導致的。表一給出了微軟若干由于不當使用C動態(tài)庫函數(shù)而導致的安全漏洞【1p242】。 

            微軟安全公告 涉及產(chǎn)品 涉及的函數(shù)
            MS02-039 Microsoft SQL Server 2000 sprint
            MS05-010 Microsoft License Server lstrcpy
            MS04-011 Microsoft Windows (DCPromo) wvsprintf
            MS04-011 Microsoft Windows (MSGina) lstrcpy
            MS04-031 Microsoft Windows (NetDDE) wcscat
            MS03-045 Microsoft Windows (USER) wcscpy

            1:不當使用C動態(tài)庫函數(shù)而導致的安全漏洞

               不當使用C動態(tài)庫函數(shù)容易引入安全漏洞,這一點并不奇怪。C動態(tài)庫函數(shù)的設計大約是30年前的事情了。當時,安全方面的考慮并不是設計上需要太多注意的地方。

              2.2 危險API的列表


             

               有關完整的危險API的禁用列表,大家可以參見http://msdn.microsoft.com/en-us/library/bb288454.aspx. 

               在這里我們列出其中的一部分,以便大家對那些API被禁用有所體會。 

            <DIV align=left>
            禁用的API 替代的StrSafe函數(shù) 替代的Safe CRT函數(shù)
            有關字符串拷貝的API
            strcpy, wcscpy, _tcscpy, _mbscpy, StrCpy, StrCpyA, StrCpyW, lstrcpy, lstrcpyA, lstrcpyW, strcpyA, strcpyW, _tccpy, _mbccpy StringCchCopy, StringCbCopy,

            StringCchCopyEx, StringCbCopyEx

            strcpy_s
            有關字符串合并的API
            strcat, wcscat, _tcscat, _mbscat, StrCat, StrCatA, StrCatW, lstrcat, lstrcatA, lstrcatW, StrCatBuffW, StrCatBuff, StrCatBuffA, StrCatChainW, strcatA, strcatW, _tccat, _mbccat StringCchCat, StringCbCat,

            StringCchCatEx, StringCbCatEx

            strcat_s
            有關sprintfAPI
            wnsprintf, wnsprintfA, wnsprintfW, sprintfW, sprintfA, wsprintf, wsprintfW, wsprintfA, sprintf, swprintf, _stprintf StringCchPrintf, StringCbPrintf,

            StringCchPrintfEx, StringCbPrintfEx

            _snprintf_s

            _snwprintf_s

            </DIV>

            2:禁用API的列表(部分)

               其它被禁用的API還有scanf, strtok, gets, itoa等等。 ”n”系列的字符串處理函數(shù),例如strncpy等,也在被禁用之列。

            1. 如何替代被禁用的危險API

               從上面的介紹可以看出絕大多數(shù)C動態(tài)庫中的字符串處理函數(shù)都被禁用。那么,如何在代碼中替代這些危險的API呢?在表2,我們看到有兩種替代方案:

              • StrSafe
              • Safe CRT


             

               后面我們會討論這兩種方案的不同之處。這里我們先說它們的共同點:提供更安全的字符串處理功能。特別在以下幾個方面:

              • 目標緩存區(qū)的大小被顯式指明。
              • 動態(tài)校驗。
              • 返回代碼。


             

               StringCchCopy舉例。它的定義如下:

            HRESULT StringCchCopy(         

                LPTSTR pszDest,

                size_t cchDest,

                LPCTSTR pszSrc

            );


             

               cchDest指明目標緩存區(qū)pszDest最多能容納字符的數(shù)目,其值必須在1STRSAFE_MAX_CCH之間。StringCchCopy總是確保pszDest被拷貝的字符串是以NULL結(jié)尾。并且提供以下的返回代碼: S_OKSTRSAFE_E_INVALID_PARAMETER,和STRSAFE_E_INSUFFICIENT_BUFFER。這樣,采用StringCchCopy來替代被禁用的strcpy的話,就可以有效降低由于誤用字符串拷貝而導致緩存溢出的可能。

              3.1使用StrSafe


             

               使用StrSafe非常簡單。在C/C++代碼中加入以下的頭文件即可。

            #include "strsafe.h"


             

               StrSafe.h包含在Windows Platform SDK中。用戶可以通過在微軟的網(wǎng)站直接下載。 

               下面給出一個使用StrSafe的代碼示例【2】。

               不安全的代碼:

            void UnsafeFunc(LPTSTR szPath,DWORD cchPath) {

               TCHAR szCWD[MAX_PATH];

               GetCurrentDirectory(ARRAYSIZE(szCWD), szCWD);

               strncpy(szPath, szCWD, cchPath);

               strncat(szPath, TEXT("\\"), cchPath);

               strncat(szPath, TEXT("desktop.ini"),cchPath);

            }


             

               在以上代碼里存在著幾個問題:首先,沒有錯誤代碼的校驗。更嚴重的是,在strncat中,cchPath是目標緩存區(qū)可以存放字符的最大數(shù)目,而正確傳遞的參數(shù)應該是目標緩存區(qū)剩余的字符數(shù)目。 

               使用StrSafe后的代碼是

            bool SaferFunc(LPTSTR szPath,DWORD cchPath) {

               TCHAR szCWD[MAX_PATH];

               if (GetCurrentDirectory(ARRAYSIZE(szCWD), szCWD)            &&

                  SUCCEEDED(StringCchCopy(szPath, cchPath, szCWD))    &&

                  SUCCEEDED(StringCchCat(szPath, cchPath, TEXT("\\"))) &&

                  SUCCEEDED(StringCchCat(szPath, cchPath, TEXT("desktop.ini")))) {

                     return true;

               }

               return false;

            }

              3.2使用Safe CRT


             

               SafeCRTVisual Studio 2005起開始支持。當代碼中使用了禁用的危險的CRT函數(shù),Visual Studio 2005編譯時會報告相應警告信息,以提醒開發(fā)人員考慮將其替代為Safe CRT中更為安全的函數(shù)。 

               下面給出一個使用Safe CRT的代碼示例【3】。 

               不安全的代碼:

            void UnsafeFunc (const wchar_t * src)

            {

                // Original

                wchar_t dest[20];

                wcscpy(dest, src);           // 編譯警告

                wcscat(dest, L"...");        // 編譯警告

            }


             

               以上這段代碼里存在著明顯緩存溢出的問題。 

               使用Safe CRT后的代碼是

            errno_t SaferFunc(const wchar_t * src)

            {

                wchar_t dest[20];

               

                errno_t err = wcscpy_s(dest, _countof(dest), src);

                if (!err)

                    return err;

                return wcscat_s(dest, _countof(dest), L"...");

            }

              3.3 StrSafe Safe CRT的對比

               我們看到,StrSafeSafe CRT存在功能重疊的地方。那么什么時候使用StrSafe什么時候使用SafeCRT呢?

               下面的表格【1p246】里列出了兩者之間的差異。采用何種方式應該根據(jù)具體情況而定。有時候也許只能采取其中一種方式:例如如果你的開發(fā)系統(tǒng)是Visual Studio 2003的話,就只能使用StrSafe。或者你的代碼中有許多itoa的話,就考慮使用Safe CRT,因為StrSafe中沒有提供簡單的替代方式。有時候也許兩者都可以。這種情況下,我個人是更喜歡采用StrSafe這種方式,因為它不依賴具體的動態(tài)庫支持。如果是編寫Win32上的程序的話,StrSafeHRESULT的返回代碼,也和Win32 API的代碼類似,這樣代碼的整體風格可能會更加一致。

            <DIV align=left>
              StrSafe Safe CRT
            發(fā)布方式 Web Microsoft Visual Studio 2005
            頭文件 一個 (StrSafe.h) 多個 (不同的 C runtime 頭文件)
            是否提供鏈接庫的版本
            是否提供內(nèi)嵌(Inline)版本
            是否是業(yè)界標準 正在評估過程
            Kernel Mode支持
            返回類型 HRESULT (user mode)

            NTSTATUS (kernel mode)

            隨函數(shù)變化,errno_t
            是否需要修改代碼
            主要針對 緩存溢出 緩存溢出,和其它安全方面的考慮
            </DIV>

            3:StrSafe和Safe CRT對比

            1. 爭論

                在開發(fā)過程中,代碼中全面禁用危險的API的編碼實踐,存在著一定的爭議性。其中最具有代表性的觀點可以參見Danny KalevVisual C++ 8.0 Hijacks the C++ Standard一文【4】。爭論主要集中在以下幾點。

              4.1對于程序性能的影響

                StrSafe舉例,由于增加了更多的動態(tài)校驗,其速度較C動態(tài)庫的函數(shù)相比,是有所下降的。在【2】一文中,給出了對StrSafe速度方面的測試數(shù)據(jù)如下: 

                測試例子:1千萬次字符串合并調(diào)用。結(jié)果:

                C動態(tài)庫:7.3

                StrSafe8.3 

                我們看到,如果開發(fā)的系統(tǒng)不是完全以字符串處理為工作核心的話,使用StrSafe對系統(tǒng)性能的影響是可以控制的。

              4.2開發(fā)人員可以決定在合適的地方使用危險API


             

                首先,同意如果代碼中正確使用危險API的話,也是可以避免安全漏洞的引入。但是,在具體的開發(fā)實踐中,存在著以下問題:

              • 開發(fā)人員的素質(zhì)和培訓
              • 有時候即使執(zhí)行嚴格的代碼復查,仍然可能由于使用危險的API引入安全漏洞。


             

                第二點尤其關鍵。大家看到這里可能會有疑問,使用危險的API有這么容易出問題嗎?即便代碼復查(code review)也沒能看出來?【5中給出了一個微軟安全漏洞的具體實例。 

                微軟 05-047 Plug-n-Play RPC:即插即用中的漏洞,允許遠程執(zhí)行代碼和特權(quán)提升。經(jīng)過身份驗證的攻擊者可以通過創(chuàng)建特制的網(wǎng)絡消息并將該消息發(fā)送到受影響的系統(tǒng)來嘗試利用此漏洞。導致這個嚴重的安全漏洞的代碼如下:

            #define MAX_CM_PATH                       360

            GetInstanceList(

            IN LPCWSTR pszDevice, IN OUT LPWSTR *pBuffer, IN OUT PULONG pulLength)

            {

                  WCHAR       RegStr[MAX_CM_PATH], szInstance[MAX_DEVICE_ID_LEN];

            ...

                  // Validate that passed in pszDevice is an actual registry entry

                  // If lookup for the key fails, reject call and cleanup.

                  // ghEnumKey points to HKLM\System\CurrentControlSet\Enum

                  if (RegOpenKeyEx(ghEnumKey, pszDevice, 0,

                    KEY_ENUMERATE_SUB_KEYS, &hKey) != ERROR_SUCCESS) {

             Status = CR_REGISTRY_ERROR;

                  goto Clean0;

            }

            ...

                  ulLen = MAX_DEVICE_ID_LEN;  // size in chars

            ...

                  // Query szInstance from registry

                  RegStatus = RegEnumKeyEx(hKey, ulIndex, szInstance, &ulLen, ...);

                  if (RegStatus == ERROR_SUCCESS) {

                  // Build lookup string given a valid registry root key and valid instance ID

                  wsprintf(RegStr, TEXT("%s\\%s"), pszDevice, szInstance);}


             

                復查這段代碼時,我們看到,雖然使用了危險的APIwsprintf,但應該是不會發(fā)生緩存溢出的問題。這是因為根據(jù)MSDN

             

             

                

            1:注冊表字符數(shù)目的限制

                于是:

              • pszDevice 應該少于255 characters
              • pszDevice 是一個 有效的 key HKLM\System\CurrentControlSet\Enum
              • szInstance 是一個有效的subkeypszDevice
              • RegStr is 360 characters
              • 攻擊者并不能控制注冊表內(nèi)容


             

                但實際上,wspringf還是導致了緩存溢出的安全漏洞。到底是怎么回事?我們來看一下攻擊代碼:

            errno_t SaferFunc(const wchar_t * src)

            int main()

            {

                  PWCHAR pszFilter = (PWCHAR)malloc(sizeof(WCHAR)*1000);

                  PWCHAR Buffer = (PWCHAR)malloc(86);

                  wsprintf(pszFilter,L"ISAPNP\\ReadDataPort\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\0");

                  CM_Get_Device_ID_List((PCWSTR)pszFilter,Buffer,86,1);

                  return 0;

            }


             

                攻擊代碼之所以有效,是因為:

              • pszFilter(除去末位的0) 作為pszDevice 傳遞給GetInstanceList
              • RegOpenKeyEx 接收了這個長字符串,忽略那些“\”,返回 ERROR_SUCCESS


             

                通過這個例子我們看出,在開發(fā)復雜的系統(tǒng)中,即便是有經(jīng)驗的開發(fā)人員,加上嚴格的代碼復查過程,還是有可能由于使用危險的API而導致安全漏洞的引入。這也是微軟在Windows Vista的開發(fā)過程中全面禁用危險API的原因。

              4.3程序可移植性的影響

               這一點的考慮是非常值得重視的。不管是StrSafe還是Safe CRT,都不是工業(yè)界標準。因此,如果開發(fā)的系統(tǒng)需要移植到其它平臺的話,采用Safe CRT是肯定不合適的。StrSafeInline方式,因為不依賴特定庫,對可移植性的影響相對較小。

            1. 總結(jié)

               C/C++程序中禁用危險的API,可以有效降低在代碼中引入安全漏洞的可能。在考慮了性能和可移植性的因素下,強烈建議在開發(fā)過程中,使用StrSafe或Safe CRT中對應的安全函數(shù)來替代被禁用的危險的API調(diào)用。

            1. 參考文獻

            Feedback

            # re: C/C++禁用危險API  回復  更多評論   

            2011-03-21 20:22 by yautou
            寫的很好,不介意小弟轉(zhuǎn)載博客吧,值得收藏,!
            丁香五月综合久久激情| 99久久精品免费看国产一区二区三区 | 伊人久久大香线蕉综合热线| 久久久久一本毛久久久| 久久精品无码一区二区WWW| 久久综合精品国产二区无码| 色综合久久天天综合| 日韩欧美亚洲综合久久| 久久国产乱子精品免费女| 色天使久久综合网天天| AAA级久久久精品无码区| 国产亚洲美女精品久久久2020| 91久久精一区二区三区大全| 亚洲欧美一区二区三区久久| 久久国产精品久久国产精品| 99久久夜色精品国产网站| 青青青青久久精品国产h久久精品五福影院1421 | 久久精品嫩草影院| 久久婷婷是五月综合色狠狠| 国产亚洲精久久久久久无码| 亚洲一区精品伊人久久伊人| 久久精品国产亚洲AV无码娇色| 亚洲国产成人精品无码久久久久久综合 | 欧美与黑人午夜性猛交久久久| 国产精品久久久久影院嫩草| 久久AV高潮AV无码AV| 婷婷久久综合九色综合绿巨人| 婷婷综合久久中文字幕| 69久久夜色精品国产69| 少妇高潮惨叫久久久久久| 色诱久久av| 亚洲中文字幕伊人久久无码 | 久久久精品久久久久影院| 99久久精品无码一区二区毛片| 精品久久久久久久无码| 久久超碰97人人做人人爱| 久久精品国产亚洲av麻豆色欲| 99精品国产99久久久久久97| 亚洲国产精品无码久久一区二区| 久久精品国产清自在天天线| 亚洲色大成网站www久久九|