《华为CC++语言安全规范》笔记
《華為C&C++語言安全規(guī)范》筆記
通過閱讀《華為C&C++語言安全規(guī)范》1,我了解到了我在編程中很多缺失的部分。現(xiàn)在記錄下幾個(gè)要點(diǎn):
規(guī)則1.1.4:嚴(yán)禁對指針變量進(jìn)行sizeof操作
編碼人員往往由于粗心,將指針當(dāng)做數(shù)組進(jìn)行sizeof操作,導(dǎo)致實(shí)際的執(zhí)行結(jié)果與預(yù)期不符。 下面的代碼,buffer和path分別是指針和數(shù)組,編碼人員想對這2個(gè)內(nèi)存進(jìn)行清0操作,但由于編碼人員的疏忽,第5行代碼,將內(nèi)存大小誤寫成了sizeof,與預(yù)期不符。
如果要判斷當(dāng)前的指針類型大小,請使用sizeof(char *)的方式。
相關(guān)指南:
CERT.ARR01-C. Do not apply the sizeof operator to a pointer when taking the size of an array
指針與數(shù)組名與指針有太多的相似,甚至很多時(shí)候,數(shù)組名可以作為指針使用。但是數(shù)組和指針存在差異。指針,是一個(gè)變量,存儲的數(shù)據(jù)是地址。數(shù)組名的內(nèi)涵在于其指代實(shí)體是一種數(shù)據(jù)結(jié)構(gòu),這種數(shù)據(jù)結(jié)構(gòu)就是數(shù)組,其外延在于其可以轉(zhuǎn)換為指向其指代實(shí)體的指針,而且是一個(gè)指針常量2。數(shù)組名只是大多數(shù)時(shí)候隱式轉(zhuǎn)換成指向首元素的指針類型右值。這些時(shí)候不會轉(zhuǎn)換:1)對其用 &;2)對其用 sizeof;3)C++中取引用3。
我們先來一段代碼作為演示:
#include<iostream> using namespace std; void func(int C[]) {cout<<"In function, C sizeof(C):"<<sizeof(C)<<endl;cout<<"C point:"<<C<<endl;cout<<"C &point:"<<&C<<endl;C++; } int main() {int A[10];int* B=new int[10];cout<<"A sizeof(A):"<<sizeof(A)<<endl;cout<<"B sizeof(B):"<<sizeof(B)<<endl;// 取引用地址cout<<"A point:"<<A<<endl;cout<<"A &point:"<<&A<<endl;cout<<"B point:"<<B<<endl;cout<<"B &point:"<<&B<<endl;//調(diào)用函數(shù)func(A);//A++;//Errorreturn 0; }在X86的編譯環(huán)境下,輸出的結(jié)果為:
A sizeof(A):40 B sizeof(B):4 A point:0x6dfec8 A &point:0x6dfec8 B point:0x1fa838 B &point:0x6dfec4 In function, C sizeof(C):4 C point:0x6dfec8 C &point:0x6dfeb0顯然第14行輸出的是數(shù)組長度,第15行輸出的是指針長度(在X86下為4字節(jié),在x64環(huán)境下為8字節(jié))。
第17-第20行,對數(shù)組取引用,其地址和本身的地址是一樣,而指針則不一樣。
第23行,當(dāng)調(diào)用函數(shù)的時(shí)候,數(shù)組會轉(zhuǎn)換為指針,因此長度為4,并且可以做自加運(yùn)算。
規(guī)則1.2.1:斷言必須使用宏定義,禁止直接調(diào)用系統(tǒng)提供的assert()
斷言只能在調(diào)試版使用,斷言被觸發(fā)后,程序會立即退出,因此嚴(yán)禁在正式發(fā)布版本使用斷言,請通過編譯選項(xiàng)進(jìn)行控制。 錯(cuò)誤用法如:
int Foo(int *array, int size) {assert(array != NULL);... }ASSERT()是MFC的宏4,ASSERT只有在Debug版本中才有效,如果編譯為Release版本則被忽略。
assert()的功能類似,它是ANSI C標(biāo)準(zhǔn)中規(guī)定的函數(shù),它與ASSERT的一個(gè)重要區(qū)別是可以用在Release版本中5。
在msvc16里面是這樣定義的:
#undef assert#ifdef NDEBUG#define assert(expression) ((void)0)#else_ACRTIMP void __cdecl _wassert(_In_z_ wchar_t const* _Message,_In_z_ wchar_t const* _File,_In_ unsigned _Line);#define assert(expression) (void)( \(!!(expression)) || \(_wassert(_CRT_WIDE(#expression), _CRT_WIDE(__FILE__), (unsigned)(__LINE__)), 0) \)#endif在MinGW里面
/* According to C99 standard (section 7.2) the assertmacro shall be redefined each time assert.h getsincluded depending on the status of NDEBUG macro. */ #undef assert ... #ifdef NDEBUG #define assert(_Expression) ((void)0) #else /* !defined (NDEBUG) */ #if defined(_UNICODE) || defined(UNICODE) #define assert(_Expression) \(void) \((!!(_Expression)) || \(_wassert(_CRT_WIDE(#_Expression),_CRT_WIDE(__FILE__),__LINE__),0)) #else /* not unicode */ #define assert(_Expression) \(void) \((!!(_Expression)) || \(_assert(#_Expression,__FILE__,__LINE__),0)) #endif /* _UNICODE||UNICODE */ #endif /* !defined (NDEBUG) */綜上所述,使用語言自帶的assert即可,也沒有其他的選擇。當(dāng)在Release環(huán)境下,assert也自動編譯為((void)0)。對于華為的這條安全規(guī)范表示不解,希望有人能解答一下。
另外:這里還發(fā)現(xiàn)很有意思的東西:
/* According to C99 standard (section 7.2) the assert
macro shall be redefined each time assert.h gets
included depending on the status of NDEBUG macro. */
意思就是每次包含<assert.h>時(shí),都會根據(jù)NDEBUG的當(dāng)前狀態(tài)重新定義assert宏6。
規(guī)則1.2.3:嚴(yán)禁在斷言內(nèi)改變運(yùn)行環(huán)境
在程序正式發(fā)布階段,斷言不會被編譯進(jìn)去,為了確保調(diào)試版和正式版的功能一致性,嚴(yán)禁在斷言中使用任何賦值、修改變量、資源操作、內(nèi)存申請等操作。 例如,以下的斷言方式是錯(cuò)誤的:
ASSERT(p1 = p2); //p1被修改 ASSERT(i++ > 1000); //i被修改 ASSERT(close(fd) == 0);//fd被關(guān)閉建議1.2.1:不要將多條語句放在同一個(gè)斷言中
為了更加準(zhǔn)確地發(fā)現(xiàn)錯(cuò)誤的位置,每一條斷言只校驗(yàn)一個(gè)條件。 下面的斷言同時(shí)校驗(yàn)多個(gè)條件,在斷言觸發(fā)的時(shí)
候,無法判斷到底是哪一個(gè)條件導(dǎo)致的錯(cuò)誤:
應(yīng)該將每個(gè)條件分開:
int Foo(int *array, int size) {ASSERT(array != NULL);ASSERT(size > 0);ASSERT(size < MAX_SIZE);... }規(guī)則1.3.2:嚴(yán)禁對公共接口API函數(shù)的參數(shù)進(jìn)行ASSERT操作
對于設(shè)計(jì)成API的函數(shù),必須對參數(shù)進(jìn)行合法性判斷,嚴(yán)禁在API實(shí)現(xiàn)過程中產(chǎn)生RASH。對API函數(shù)的參數(shù)進(jìn)行ASSERT操作是沒有意義的。 例如,對于提供應(yīng)用服務(wù)器IP的平臺公共API接口這樣實(shí)現(xiàn)是錯(cuò)誤的:
int GetServerIP(char *ip, size_t ipSize) {ASSERT(ip != NULL);... }公共接口API應(yīng)當(dāng)對輸入?yún)?shù)進(jìn)行代碼檢查:
int GetServerIP(char *ip, size_t ipSize) {if (ip == NULL) {...}... }建議1.3.1:謹(jǐn)慎使用不可重入函數(shù)
不可重入函數(shù)在多線程環(huán)境下其執(zhí)行結(jié)果不能達(dá)到預(yù)期效果,需謹(jǐn)慎使用。常見的不可重入函數(shù)包括:
rand, srand
getenv, getenv_s
strtok
strerror
asctime, ctime, localtime, gmtime
setlocale
atomic_init
tmpnam
mbrtoc16, c16rtomb, mbrtoc32, c32rtomb
gethostbyaddr
gethostbyname
inet_ntoa
7
建議1.3.2:字符串或指針作為函數(shù)參數(shù)時(shí),請檢查參數(shù)是否為NULL
如果字符串或者指針作為函數(shù)參數(shù),為了防止空指針引用錯(cuò)誤,在引用前必須確保該參數(shù)不為NULL,如果上層調(diào)用者已經(jīng)保證了該參數(shù)不可能為NULL,在調(diào)用本函數(shù)時(shí),在函數(shù)開始處可以加ASSERT進(jìn)行校驗(yàn)。 例如下面的代碼,因?yàn)锽YTE *p有可能為NULL,因此在使用前需要進(jìn)行判斷。
int Foo(int *p, int count) {if (p != NULL && count > 0) {int c = p[0];}... } int Foo2() {int *arr = ...int count = ...Foo(arr, count);... }下面的代碼,由于p的合法性由調(diào)用者保證,對于Foo函數(shù),不可能出現(xiàn)p為NULL的情況,因此加上ASSERT進(jìn)行校驗(yàn)。
int Foo(int *p, int count) {ASSERT(p != NULL); //ASSERT is added to verify p.ASSERT(count > 0);int c = p[0];...} int Foo2() {int *arr = ...int count = ......if (arr != NULL && count > 0) {Foo(arr, count);}... }規(guī)則1.5.1:禁用C++異常機(jī)制
嚴(yán)禁使用C++的異常機(jī)制,所有的錯(cuò)誤都應(yīng)該通過錯(cuò)誤值在函數(shù)之間傳遞并做相應(yīng)的判斷, 而不應(yīng)該通過異常機(jī)制進(jìn)行錯(cuò)誤處理。 編碼人員必須完全掌控整個(gè)編碼過程,建立攻擊者思維,增強(qiáng)安全編碼意識,主動把握有可能出錯(cuò)的環(huán)節(jié)。而使用C++異常機(jī)制進(jìn)行錯(cuò)誤處理,會削弱編碼人員的安全意識。 異常機(jī)制會打亂程序的正常執(zhí)行流程,使程序結(jié)構(gòu)更加復(fù)雜,原先申請的資源可能會得不到有效清理。 異常機(jī)制導(dǎo)致代碼的復(fù)用性降低,使用了異常機(jī)制的代碼,不能直接給不使用異常機(jī)制的代碼復(fù)用。 異常機(jī)制在實(shí)現(xiàn)上依賴于編譯器、操作系統(tǒng)、處理器,使用異常機(jī)制,導(dǎo)致程序執(zhí)行性能降低。 在二進(jìn)制層面,程序被加載后,異常處理函數(shù)增加了程序的被攻擊面,攻擊者可以通過覆蓋異常處理函數(shù)地址,達(dá)到攻擊的效果。 例外: 在接管C++語言本身拋出的異常(例如new失敗、STL)、第三方庫(例如IDL)拋出的異常時(shí),可以使用異常機(jī)制,例如:
int len = ...; char *p = NULL; try {p = new char[len]; } catch (bad_alloc) {...abort(); }相關(guān)指南:
Google C++ Style Guide.Exceptions: We do not use C++ exceptions.
這點(diǎn)在《游戲引擎架構(gòu)8》中也提到過。
規(guī)則1.6.3:嚴(yán)禁在構(gòu)造函數(shù)中創(chuàng)建線程
構(gòu)造函數(shù)內(nèi)僅作成員變量的初始化工作,其他的操作通過成員函數(shù)完成。
規(guī)則1.6.5:如果類的公共接口中返回類的私有數(shù)據(jù)地址,則必須加const類
型
實(shí)例:
class CMsg { public:CMsg();~CMsg();Const unsigned char *GetMsg(); protected:int size;unsigned char *msg; }; CMsg::CMsg() {size = 0;msg = NULL; } const unsigned char *CMsg::GetMsg() {return msg; }規(guī)則1.7.3:禁用pthread_exit、ExitThread函數(shù)
嚴(yán)禁在線程內(nèi)主動終止自身線程,線程函數(shù)在執(zhí)行完畢后會自動、安全地退出。主動終止自身線程的操作,不僅導(dǎo)致代碼復(fù)用性變差,同時(shí)容易導(dǎo)致資源泄漏錯(cuò)誤。
建議1.7.1:禁用exit、ExitProcess函數(shù)(main函數(shù)除外)
程序應(yīng)該安全退出,除了main函數(shù)以外,禁止任何地方調(diào)用exit、ExitProcess函數(shù)退出進(jìn)程。直接退出進(jìn)程會導(dǎo)致代碼的復(fù)用性降低,資源得不到有效地清理。 程序應(yīng)該通過錯(cuò)誤值傳遞的機(jī)制進(jìn)行錯(cuò)誤處理。 以下代碼加載文件,加載過程中如果出錯(cuò),直接調(diào)用exit退出:
void LoadFile(const char *filePath) {FILE* fp = fopen(filePath, "rt");if (fp == NULL) {exit(0);}... }正確的做法應(yīng)該通過錯(cuò)誤值傳遞機(jī)制,例如:
BOOL LoadFile(const char *filePath) {BOOL ret = FALSE;FILE* fp = fopen(filePath, "rt");if (fp != NULL) {...}...return ret; }建議1.7.2:禁用abort函數(shù)
abort會導(dǎo)致程序立即退出,資源得不到清理。 例外: 只有發(fā)生致命錯(cuò)誤,程序無法繼續(xù)執(zhí)行的時(shí)候,在錯(cuò)誤處理函數(shù)中使用abort退出程序,例如:
void FatalError(int sig) {abort(); } int main(int argc, char *argv[]) {signal(SIGSEGV, FatalError);... }規(guī)則2.5:調(diào)用格式化函數(shù)時(shí),禁止format參數(shù)由外部可控
調(diào)用格式化函數(shù)時(shí),如果format參數(shù)由外部可控,會造成字符串格式化漏洞。 這些格式化函數(shù)有: 格式化輸出函數(shù):xxxprintf 格式化輸入函數(shù):xxxscanf 格式化錯(cuò)誤消息函數(shù):err(),verr(),errx(),verrx(),warn(),vwarn(),warnx(),vwarnx(),error(),error_at_line(); 格式化日志函數(shù):syslog(),vsyslog()。
錯(cuò)誤示例:
推薦做法:
char *msg = GetMsg(); ... printf("%s\n", msg);相關(guān)指南:
CERT.FIO47-C. Use valid format strings
MITRE.CWE-134: Use of Externally-Controlled Format String
規(guī)則4.2:整型表達(dá)式比較或賦值為一種更大類型之前必須用這種更大類型對它進(jìn)行求值
由于整數(shù)在運(yùn)算過程中可能溢出,當(dāng)運(yùn)算結(jié)果賦值給比他更大類型,或者和比他更大類型進(jìn)行比較時(shí),會導(dǎo)致實(shí)際結(jié)果與預(yù)期結(jié)果不符。 請觀察以下二個(gè)代碼及其輸出:
int main(int argc, char *argv[]) {unsigned int a = 0x10000000;unsigned long long b = a * 0xab;printf("b = %llX\n", b);return 0; }輸出: b = B0000000
int main(int argc, char *argv[]) {unsigned int a = 0x10000000;unsigned long long b = (unsigned long long )a * 0xab;printf("b = %llX\n", b);return 0; }輸出: b = AB0000000
規(guī)則4.3:禁止對有符號整數(shù)進(jìn)行位操作符運(yùn)算
位操作符(~、>>、<<、&、^、|)應(yīng)該只用于無符號整型操作數(shù)。 錯(cuò)誤示例:
int data = ReadByte(); int a = data >> 24;推薦做法:(為簡化示例代碼,此處假設(shè)ReadByte函數(shù)實(shí)際不存在返回值小于0的情況)
unsigned int data = (unsigned int)ReadByte(); unsigned int a = data >> 24;相關(guān)指南:
CERT.INT13-C. Use bitwise operators only on unsigned operands
MISRA.C.2004.Rule 12.7 (required): Bitwise operators shall not be applied to operands whose underlyingtype is signed.
在C語言中,如果在未知的有符號上執(zhí)行位操作,很可能會導(dǎo)致緩沖區(qū)溢出,從而在某些情況下導(dǎo)致攻擊者執(zhí)行任意代碼,同時(shí),還可能會出現(xiàn)出乎意料的行為或編譯器定義的行為。
代碼如下:
#include<stdio.h> int main() {int y=0x80000000;printf("%d\n",y>>24);//以十進(jìn)制有符號形式輸出。printf("%u\n",y>>24);//以十進(jìn)制無符號形式輸出。return 0; }輸出為:
-128 4294967168由于int類型的最高位是符號位,剩下的31位才用來存儲數(shù)值,y>>24的結(jié)果應(yīng)該是0xffffff80(負(fù)數(shù)右移,左補(bǔ)1),當(dāng)我們以無符號整型輸出時(shí),為正數(shù)的0xffffff80,以有符號整型輸出時(shí),應(yīng)減一再取反,結(jié)果為-1289。
然而,在右移運(yùn)算中,空出的位用 0 還是符號位進(jìn)行填充是由于具體的 C 語言編譯器實(shí)現(xiàn)來決定。在通常情況下,如果要進(jìn)行移位的操作數(shù)是無符號類型的,那么空出的位將用 0 進(jìn)行填充;如果要進(jìn)行移位的操作數(shù)是有符號類型的,則 C 語言編譯器實(shí)現(xiàn)既可選擇 0 來進(jìn)行填充,也可選擇符號位進(jìn)行填充。
因此,如果很關(guān)心一個(gè)右移運(yùn)算中的空位,那么可以使用 unsigned 修飾符來聲明變量,這樣空位都會被設(shè)置為 0。同時(shí),如果一個(gè)程序采用了有符號數(shù)的右移位操作,那么它就是不可移植的10。
規(guī)則4.4:禁止整數(shù)與指針間的互相轉(zhuǎn)化
指針的大小隨著平臺的不同而不同,強(qiáng)行進(jìn)行整數(shù)與指針間的互相轉(zhuǎn)化,降低了程序的兼容性,在轉(zhuǎn)換過程中可能引起指針高位信息的丟失。
錯(cuò)誤示例:
推薦做法:
char *ptr = ...; uintptr_t number = (uintptr_t)ptr;相關(guān)指南:
CERT.INT36-C. Converting a pointer to integer or integer to pointer
MISRA.C.2004.Rule 11.3 (advisory): A cast should not be performed between a pointer type and an integral type.
規(guī)則4.5:禁止對指針進(jìn)行邏輯或位運(yùn)算(&&、||、!、~、>>、<<、&、^、|)
對指針進(jìn)行邏輯運(yùn)算,會導(dǎo)致指針的性質(zhì)改變,可能產(chǎn)生內(nèi)存非法訪問的問題。 下面是錯(cuò)誤的用法:
BOOL dealName(const char *nameA, const char *nameB) {...if (nameA)...if (!nameB)... }下面是正確的用法:
BOOL dealName(const char *nameA, const char *nameB) {...if (nameA != NULL)...if (nameB == NULL)... }例外: 為檢查地址對齊而對地址指針進(jìn)行的位運(yùn)算可以作為例外。
相關(guān)指南:
MISRA.C.2004.Rule 12.6 (advisory): The operands of logical operators (&&, || and !) should be effectively Boolean. Expressions that are effectively Boolean should not be used as operands to operators other than (&&, ||, !, =, ==, != and ?😃.
規(guī)則5.1:內(nèi)存申請前,必須對申請內(nèi)存大小進(jìn)行合法性校驗(yàn)
內(nèi)存申請的大小可能來自于外部數(shù)據(jù),必須檢查其合法性,防止過多地、非法地申請內(nèi)存。不能申請0長度的內(nèi)存。 例如:
int Foo(int size) {if (size <= 0) {//error...}...char *msg = (char *)malloc(size);... }相關(guān)指南:
CERT.MEM04-C. Beware of zero-length allocations
MITRE.CWE-789: Uncontrolled Memory Allocation
規(guī)則5.2:內(nèi)存分配后必須判斷是否成功
char *msg = (char *)malloc(size); if (msg != NULL) {... }相關(guān)指南:
CERT.MEM11-C. Do not assume infinite heap space
CERT.ERR33-C. Detect and handle standard library errors
CERT.MEM52-CPP. Detect and handle memory allocation errors
MITRE.CWE 252, Unchecked Return Value
MITRE.CWE 391, Unchecked Error Condition
MITRE.CWE 476, NULL Pointer Dereference
MITRE.CWE 690, Unchecked Return Value to NULL Pointer Dereference
MITRE.CWE 703, Improper Check or Handling of Exceptional Conditions
MITRE.CWE 754, Improper Check for Unusual or Exceptional Conditions
規(guī)則5.3:禁止引用未初始化的內(nèi)存
malloc、new分配出來的內(nèi)存沒有被初始化為0,要確保內(nèi)存被引用前是被初始化的。 以下代碼使用malloc申請內(nèi)存,在使用前沒有初始化:
int *CalcMetrixColomn( int **metrix ,int *param, size_t size ) {int *result = NULL;...size_t bufSize = size * sizeof(int);...result = (int *)malloc(bufSize);...result[0] += metrix[0][0] * param[0];...return result; }以下代碼使用memset_s()對分配出來的內(nèi)存清零。
int *CalcMetrixColomn(int **metrix ,int *param, size_t size) {int *result = NULL;...size_t bufSize = size * sizeof(int);...result = (int *)malloc(bufSize);...int ret = memset_s(result, bufSize, 0, bufSize); //【修改】確保內(nèi)存被初始化后才被引用...result[0] += metrix[0][0] * param[0];...return result; }相關(guān)指南:
CERT.EXP33-C. Do not read uninitialized memory
CERT.EXP53-CPP. Do not read uninitialized memory
規(guī)則5.4:內(nèi)存釋放之后立即賦予新值
懸掛指針可能會導(dǎo)致雙重釋放(double-free)以及訪問已釋放內(nèi)存的危險(xiǎn)。消除懸掛指針以及消除眾多與內(nèi)存相關(guān)危險(xiǎn)的一個(gè)最為有效地方法就是當(dāng)指針使用完后將其置新值。 如果一個(gè)指針釋放后能夠馬上離開作用域,因?yàn)樗呀?jīng)不能被再次訪問,因此可以無需對其賦予新值。
示例:
相關(guān)指南:
CERT.MEM01-C. Store a new value in pointers immediately after free()
CERT.MEM50-CPP. Do not access freed memory
規(guī)則5.5:禁止使用realloc()函數(shù)
realloc()原型如下:
void *realloc(void *ptr, size_t size);隨著參數(shù)的不同,其行為也是不同。 1) 當(dāng)ptr不為NULL,且size不為0時(shí),該函數(shù)會重新調(diào)整內(nèi)存大小,并將新的內(nèi)存指針返回,并保證最小的size的內(nèi)容不變; 2) 參數(shù)ptr為NULL,但size不為0,那么行為等同于malloc(size); 3) 參數(shù)size為0,則realloc的行為等同于free(ptr)。 由此可見,一個(gè)簡單的C函數(shù),卻被賦予了3種行為,這不是一個(gè)設(shè)計(jì)良好的函數(shù)。雖然在編碼中提供了一些便利性,但是卻極易引發(fā)各種bug。
相關(guān)指南:
CERT.MEM36-C. Do not modify the alignment of objects by calling realloc()
規(guī)則5.6:禁止使用alloca()函數(shù)申請棧上內(nèi)存
POSIX和C99均未定義alloca()的行為,在有些平臺下不支持該函數(shù),使用alloca會降低程序的兼容性和可移植性,該函數(shù)在棧幀里申請內(nèi)存,申請的大小很可能超過棧的邊界,影響后續(xù)的代碼執(zhí)行。 請使用malloc或new,從堆中動態(tài)分配內(nèi)存。
規(guī)則7.1:創(chuàng)建文件時(shí)必須顯式指定合適的文件訪問權(quán)限
創(chuàng)建文件時(shí),如果不顯式指定合適訪問權(quán)限,可能會讓未經(jīng)授權(quán)的用戶訪問該文件。 下列代碼沒有顯式配置文件的訪問權(quán)限。
int fd = open(fileName, O_CREAT | O_WRONLY); //【錯(cuò)誤】缺少訪問權(quán)限設(shè)置推薦做法:
int fd = open(fileName, O_CREAT | O_WRONLY, S_IRUSR|S_IWUSR);規(guī)則8.3:嚴(yán)禁使用string類存儲敏感信息
string類是C++內(nèi)部定義的字符串管理類,如果口令等敏感信息通過string進(jìn)行操作,在程序運(yùn)行過程中,敏感信息可能會散落到內(nèi)存的各個(gè)地方,并且無法清0。 以下代碼,Foo函數(shù)中獲取密碼,保存到string變量password中,隨后傳遞給VerifyPassword函數(shù),在這個(gè)過程中,password實(shí)際上在內(nèi)存中出現(xiàn)了2份。
int VerifyPassword(string password) {//... } int Foo() {string password = GetPassword();VerifyPassword(password);... }應(yīng)該使用char或unsigned char保存敏感信息,如下代碼:
int VerifyPassword(const char *password) {//... } int Foo() {char password[MAX_PASSWORD] = {0};GetPassword(password, sizeof(password));VerifyPassword(password);... }《華為C&C++語言安全規(guī)范》 https://www.jb51.net/books/720904.html ??
C/C++數(shù)組名與指針區(qū)別深入探索_ljob2006的博客-CSDN博客 https://blog.csdn.net/ljob2006/article/details/4872167 ??
c中,數(shù)組名跟指針有區(qū)別嗎? - 知乎 https://www.zhihu.com/question/41805285 ??
大小寫 ASSERT 有什么區(qū)別_百度知道 https://zhidao.baidu.com/question/110720542.html ??
問題: 什么是ASSERT()? ASSERT()和assert()的區(qū)別是什么?_流風(fēng)的專欄-CSDN博客 https://blog.csdn.net/procedurecode/article/details/1942115 ??
第一章assert.h_暮秋小屋-CSDN博客 https://blog.csdn.net/qq_49150256/article/details/112753240 ??
可重入函數(shù)對于線程安全的意義(附函數(shù)表) - 云+社區(qū) - 騰訊云 https://cloud.tencent.com/developer/article/1685935 ??
游戲引擎架構(gòu) (豆瓣) https://book.douban.com/subject/25815142/ ??
有符號執(zhí)行位操作導(dǎo)致的BUG_Tonson_的博客-CSDN博客 https://blog.csdn.net/weixin_44444450/article/details/109668780 ??
位操作及其使用注意事項(xiàng),C語言位操作及其使用方法詳解 http://c.biancheng.net/view/362.html ??
總結(jié)
以上是生活随笔為你收集整理的《华为CC++语言安全规范》笔记的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Linux--安装iRedMail惊魂记
- 下一篇: 蜜蜂路线问题C++