代碼示例
B.1 概述
本附錄給出了第6章、第7章、第8章、第9章代碼安全審計要求中個審計條款代碼不規范用法示例和規范用法示例。
B.2 安全功能
B.2.1 關鍵狀態數據被外部控制代碼示例
避免關鍵狀態數據被外部控制,以下給出了不規范用法(C語言)示例。
示例:
# define DIR "/restricted/directory"
charcmd[500]
sprintf(cmd,"ls -1%480s",DIR);
RaisePrivileges(…);
system(cmd);
DropPrivieges(…);
上述代碼本意是執行一個命令,顯示一個受限制目錄的內容,然后執行其他操作。假定它是通過運行 setuid 權限來繞過操作系統的權限檢查。攻擊者只能查看 DIR目錄下的內容,程序并沒有修改“PATH”環境變量,通過設置PATH為用戶可控制的目錄(如“my/dir”),創建惡意程序“ls”并放在“my/dir”,通過運行上面的代碼,當system()被執行,shell查詢“PATH”找到“ls”程序,找到了惡意程序“/my/dir/ls”,二而找不到“bin/ls”。最終將代碼提升權限,執行了惡意程序。
B.2.2 驗證未經校驗和完整性檢查的Cookie代碼示例
是否驗證未經校驗和完整性檢查的Cookie,以下給出了不規范用法(Java語言)示例。
示例:
Cookie[]cookies = request.getCookies(); for(inti=0;i〈cookiesl.ength;i++){
Cookie c = cookies[i];
if(c.getName().equals("role")){
userRole = c.getValue();
}
}
上述代碼示例從瀏覽器 Cookie 讀取一個值確定用戶的角色,攻擊者容易修改本地存儲 Cookie 中的“role”值,可能造成特權升級。
B.2.3 以大小寫混合的方式繞過凈化和驗證代碼示例
對于防止以大小寫混合的方式繞過凈化和驗證的情況,以下給出了不規范用法(Java語言)示例。
示例:
publicStringpreventXSS(Stringinput,Stringmask){
returninput.replaceAl("script",mask);
}
上述代碼示例只有當輸入為“script”時,代碼才會執行,而當輸入為“SCRIPT”或者“ScRiPt”時并不會通過該方法進行過濾,將造成 XSS 攻擊。
B.2.4 對 HTTP頭的Web 腳本語法中的特殊字符進行過濾和驗證代碼示例
對 HTTP 頭的 Web 腳本語法中的特殊字符進行過濾和驗證,以下給出了不規范用法(Java語言)示例。
示例:
response.addHeader(HEADER_NAME,untrustedRawInputData);
上述代碼示例中,用戶控制的數據被添加到 HTTP header 并返回到客戶端。由于數據沒有經過凈化,用戶將能夠執行危險腳本標簽。
B.2.5 對數據結構控制域的刪除代碼示例
避免對數據結構控制域的刪除,以下給出了不規范用法(C語言)示例。
示例:
char*foo;
intcounter;
foo=calloc(sizeofchar *10 );
for(counter=0;counter!=10;counter++ )
{
foo[counter]= 'a';
}
printf("%s\n" foo );
上述代碼示例創建一個 null 結尾字符串并打印內容,字符串 foo 為 9 個字符和一個 null 終結符提供空間,但是 10 個字符被寫入 foo 結果,字符 foo 沒有 null 終止,調用 printf()時,將可能產生不可預知的結果。
B.2.6 對數據結構控制域的意外增加代碼示例
避免對數據結構控制域的意外增加,以下給出了不規范用法(C語言)示例。
示例:
char*foo;
foo= mal loc(sizeof(char) * 5 ) ;
foo[0]= 'a';
foo[1]= 'a';
foo[2]= atoi(getc(stdin ));
foo[3]= 'c' ;
foo[4]= '\0' ;
printf("%c %c %c %c %c\n" ,foo[0],foo[1],foo[2],foo[3],foo[4]);
printf( " %s\n " ,foo ) ;
上述代碼第一個 printf 語句將帶引每個字符,由空格分隔。若非整形數據通過 getc 從 stdin 中讀取,則 atoi 將不進行轉換,并返回 0 。當 foo 作為字符串時,字符 foo[2] 中的 0 將作為 NULL 終止符,foo[3] 將永遠不會被打印。
B.2.7 字符串的存儲具有足夠空間容納字符數據和結尾符代碼示例
保證字符串的存儲具有足夠空間容納字符數據和結尾符代碼示例,示例1給出了不規范用法(Java語言)示例,示例2給出了規范用法(C語言)示例。
示例1:
void copy(size_t n,charsrc[n],char dest[n])
{
size_ti;
for ( i = 0 ; src [i ] && (i 〈 n ) ; + + i )
{
dest[i ]=src[i ]
}
dest [i ] =' \ 0' ;
}
上述代碼中的循環把數據從 src 復制到 dest ,但終止符可能被不正確地寫到 dest 尾部之后的字節中。
示例2:
void copy(size_t n,char src[n],char dest[n])
{
size_ti ;
for ( i = 0 ; src [i ] & & (i 〈 n - 1 ) ; + + i )
{
dest[i ]=src[i] ;
}
dest[ i ]=' \0' ;
}
該方案對循環終止條件進行修改,在 dest 的尾部添加終止符。
B.2.8 對環境變量的長度做出假設代碼示例
不對環境變量的長度做出假設,示例1給出了不規范用法(C語言)示例,示例2給出了規范用法(C語言)示例。
示例1:
void f()
{
char path[PATH_MAX];
strcpy(path,getenv("PATH" ));
}
上述代碼把 getenv()返回的字符串復制到一個固定長度的緩沖區。假設 $ PATH 是已經定義了的,定義 PATH_MAX 并確保 paths 并沒有超過 PATH_MAX 的特性。環境變量 $ PATH 并不需要比 PATH_MAX 字符少,如果它超過了 PATH_MAX 字符,可能會導致緩沖區溢出。
示例2:
void f()
{
char *path = NULL;
const char*temp=getenv("PATH" ) ;
if (temp ! = NULL )
{
path= (char*) malloc(strlen(temp) +1) ;
if (path = = NULL )
{
}else{
strcpy( path,temp );
}
}
}
該方案采用 strlen()函數計算字符串的長度,并動態分配所需要的空間,避免造成緩沖區溢出。
B.2.9 將結構體的長度等同于其他各成員長度之和代碼示例
不將結構體的長度等同于其他各成員長度之和,示例給出了不規范用法(C語言)示例。
示例:
enum {buffer_size = 50 };
struct bufer{
size_t size;
char bufferC[buffer _ size ];
}buf;( )
voidfunc(conststruct bufer*buf )
{
struct bufer*buf_cpy= (struct bufer*)maloc(
sizeof (size _t ) + sizeof (buff .bufferC )
);
if (buf_cpy = = NULL )
{
memcpy(buf_cpy,buf,sizeof(struct bufer);
free(buf_cpy );
}
上述代碼假設 buffer 結構的長度等于其他各個成員的長度之和,但由于結構填充的原因,buffer 結構的實際長度可能增大。
B.2.10 數值賦值越界代碼示例
避免數值賦值越界,以下給出了不規范用法(C語言)示例。
示例:
#include〈stdio.h〉
#include〈stdbool.h〉
main(void )
{
inti;
i = -2147483648;
i = i - 1;
return(0);
}
上述代碼存在整數下溢出的問題,i 的值已是最低負值,所以減去 1 后,新的 i 值是 2 147 483 647 。
B.2.11 除零錯誤代碼示例
避免除零錯誤,示例1給出了不規范用法(C語言)示例,示例2給出了規范用法(C語言)示例。
示例1:
double divide(double x,double y )
{
return(x/y);
}
上述代碼的函數把兩個數值進行相除而沒有驗證輸入作為分母的值是否為零,將導致可能被零除的錯誤。
示例2:
double divide(double x,double y )
{
if ( 0 = = y )
{
}
return(x /y);
}
該方案通過驗證分母的輸入值,確保除零錯誤不會發生。
B.2.12 數值范圍比較時遺漏邊界值檢查代碼示例
數值范圍比較時遺漏邊界值檢查,示例1給出了不規范用法(C語言)示例,示例2給出了規范用法(C語言)示例。
示例1:
intgetValueFromArray(int *array,intlen,int index )
{
int value;
if (index〈len )
{
} else{
printf ("Error: index is :%d\n",index )
value= -1;
}
return( value);
}
上述代碼僅驗證給定的數組索引小于數組的最大長度,未檢查最小值。將可能發生負值被作為輸入的數組索引,導致越界讀取,可能造成訪問銘感內存的風險。
示例2:
if (index〉= 0 & & index 〈len )
{
}
...
該方案檢查輸入的數組索引以驗證數組在所需的最大值和最小范圍內,if 語句修改為包括最小范圍檢查。
B.2.13 采用能產生充分信息熵的算法或方案代碼示例
采用能產生充分信息熵的算法或方案,示例給出了不規范用法(C語言)示例。
示例:
longgenerateSesionID(intusrID )
{
srandom (usrID );
returnrandom();
}
上述代碼的功能是給用戶 session 產生唯一的隨機 ID 。因為偽隨機數生成器的種子永遠是用戶 ID ,所以產生的 session ID 將會永遠相同,攻擊者可以預測用戶 session ID 并劫持該 session 。
B.2.14 信息泄露代碼示例
對于避免信息泄露的情況,示例 1、示例 2 給出了不規范用法(C語言)示例。
示例1:
voidfunc(char * username,char * password )
{
if (strcmp(username,check_username ) = = 0 )
{
if ( strcmp( pasword , check_password ) ==0)
{
printf( "Login Sucesful\n" );
}else{
printf ("Login Failed-incorect password\n") ;
}
else{
printf ("Login Failed-unknown username\n" );
}
}
上述代碼示例是檢查登錄用戶名密碼是否正確的提示信息。當用戶輸入錯誤的用戶名但密碼正確和當輸入用戶名是正確的,但密碼是錯誤的情況時,反饋不同的信息。這種差異使攻擊者了解到登錄功能狀態,攻擊者可以通過嘗試不同的值,嘗試獲取正確的用戶名。
示例2:
char* path = getenv("PATH");
...
sprintf(stder,"cannot find exe on path %s\n",path);
上述代碼將打印 path 環境變量到標準錯誤流中。
B.2.15 不恰當地信任反向 DNS 代碼示例
避免不恰當地信任反向 DNS ,示例1給出了不規范用法(C語言)示例,示例2給出了規范用法(C語言)示例。
示例1:
structhostent *hp;structin_addrmyaddr;
char* tHost = " trustme .example.com " ;
myaddr.s_addr = inet_addr(ip_addr_string );
hp= gethostbyaddr((char*)&myaddrsizeof(structin_addr)AF_INET );
if(hp&& !strncmp(hp-〉h_nametHostsizeof(tHost)))
{
trusted = true;
}else{
trusted = false;
}
上述代碼使用 DNS 查找以決定入站請求是否來自受信宿主,若攻擊者損害 DNS 緩存,則可獲得受信任的狀態。
示例2:
structhostent *hp;
structin_addr myaddr;
char*tHost = "trustme.example.com";
myaddr.s_addr = inet_addr(ip_addr_string );
hp= gethostbyaddr((char*)&myaddr,sizeof(structin_addr),AF_INET );
if(hp&& !strncmp(hp-〉h_name,tHost,sizeof(tHost)))
{
/*DNS正向解析*/
if ( strcmp ( my addr.s _ addr,nslookup ( hp - 〉h_name ) ) = = 0 )
{
/*DNS反向解析*/
if (strcmp ( tHost,reverse _ nslookup ( myaddr.s _ addr ) ) = = 0 )
{
trusted = true;
}
}
}
}else{
trusted = false;
}
/*DNS正向解析*/
char * nslookup(char * hostname )
{
…
/*執行shell指令nslookup hostname 返回ip */
…
}
/*DNS正向解析*/
char * reverse_nslookup(char *ip )
{
…
/*執行shell指令nslookup -qt = ptrip 返回域名*/
…
}
該方案執行適當的正向和反向 DNS 查找,以檢測 DNS 欺騙。
B.3 代碼實現
B.3.1 混用具有泛型和非泛型的原始數據類型代碼示例

B.3.2 將不可序列化的對象存儲到磁盤上代碼示例

B.3.3 加鎖檢查缺失代碼示例

B.3.4 避免子進程使用敏感文件描述符來執行未經授權的I/O操作代碼示例

B.3.5 外部控制的格式化字符串代碼示例

B.3.6 對方法或函數參數進行驗證代碼示例

B.3.7 返回棧變量地址代碼示例

B.3.8 暴露危險的方法或代碼示例

B.3.9 使用不兼容類型的指針來訪問變量代碼示例

B.3.10 使用指針的減法來確定內存大小代碼示例

B.4 資源使用
B.4.1 遵守正確的行為次序避免早期放大攻擊數據代碼示例

B.4.2 及時釋放動態分配的內存代碼示例

B.4.3 內存緩沖區邊界操作發生越界代碼示例

B.4.4 未檢查輸入數據大小就進行緩沖區復制代碼示例

B.4.5 使用錯誤的長度值訪問緩沖區代碼示例

B.4.6 有 session 過期機制代碼示例

GB/T 39412-2020 信息安全技術 代碼安全審計規范
推薦文章: