教你幾招消滅代碼漏洞的方法
指針釋放完后必須置為空指針
指針釋放沒處理好,容易引發高風險漏洞:內存破壞漏洞。
在編程中對指針進行釋放后,需要將該指針設置為NULL,以防止后續free指針的誤用,從而導致UAF (Use After Free)等其他內存破壞問題。尤其在結構體、類里面存儲的原始指針。
錯誤釋放指針范例

正確釋放指針范例

針對指針釋放建議的解決方案:建議使用string、vector、智能指針等代替原始內存管理機制,這樣可以大量減少這類型的錯誤。
指針應用時必須檢查空指針
這類問題沒處理好,容易引發低風險的拒絕服務漏洞風險。
下面是檢查空指針范例

注意檢查指針大小的方式
檢查指針大小沒處理好,它會引發中風險邏輯漏洞的風險
下面是檢查指針大小范例

智能指針使用安全
智能指針如果沒應用好,會引發高風險漏洞:內存破壞漏洞
在編程中使用智能指針時候,必須防止智能指針和原始指針混用,否則可能會導致對象生命周期問題,例如UAF安全風險。
錯誤的使用智能指針

正確的使用智能指針

防止錯誤類型轉換
類型轉換處理不好會引發高風險的漏洞:內存破壞漏洞
在編程中對指針、對象或變量進行操作時,需要能夠正確判斷所操作對象的原始類型。如果使用了與原始類型不兼容的類型進行訪問操作,那么代碼就會存在安全的隱患。
錯誤類型轉換范例

正確使用類型轉換范例

不可直接使用無長度限制的函數
使用無長度限制的的函數,它會引發中風險漏洞和高風險漏洞:信息泄露漏洞和緩沖區溢出漏洞。
不能直接使用無長度限制的字符串拷貝、輸入函數、例如:strcpy、sprintf、wcscpy、mbscpy等函數,這些函數的特征是:通過輸入一長串字符串,而不限制長度。如果環境允許,應當使用_s安全版本替代,或者使用n版本函數(如:snprintf,vsnprintf)。
若使用形如sscanf之類的函數時,在處理字符串輸入時應該通過%10s這樣的方式來嚴格限制字符串長度,同時確保字符串末尾有\0。如果環境允許應該使用_s安全版本。
在使用n系列拷貝函數時,要確保正確計算緩沖區長度,同時,如果你不確定是否地面在各個編譯器下都能確保末尾有0時候,建議增加1字節輸入緩沖區,并將其置為\0,以確保輸出的字符串結尾一定有\0。
建議使用方案:在C++中,建議用string、vector等更高封裝層的基礎組件代替原始指針和動態數組,可以有效提高代碼的可讀性和安全性。
調用啟動進程類的系統函數的安全做法
沒調用好啟動進程類的系統函數,它會引發兩大高風險漏洞:代碼執行漏洞和權限提升漏洞。
在調用如 system、WinExec、CreateProcess、SheellExecute等啟動進程類的函數,需要嚴格檢查函數的參數。
當啟動時從用戶輸入、環境變量讀取組合命令行時,還需要注意是否可能存在命令注入風險。最好進行檢查用戶輸入是否含有非法數據。
下面可以借鑒的范例

盡量不要使用_alloca和可變長度數組
使用_alloca和可變長度數組,它可能會引發低風險和高風險漏洞:拒絕服務漏洞和內存破壞漏洞。
_alloca和可變長度數組使用的內存量在編譯期間是未知的,尤其是在循環中使用時,根據編譯器的實現不同,可能會導致:1.棧溢出;2.缺少棧內存測試的編譯器實現可能導致申請到非棧內存,并導致內存損壞。
對于C++,可變長度數組也是非標準擴展,在代碼規范中禁止使用。

調用printf系列函數,參數必須對應
調用printf系列函數沒處理好會引發中風險漏洞:信息泄露漏洞
調用printf系列函數,如sprintf,snprintf,vprintf等必須對應控制符號和參數。

不要把用戶可修改字符串作為printf系列函數的“format”參數
這個沒處理好,它會引發低風險、中風險和兩大高風險漏洞:拒絕服務、信息泄露、內存破壞和代碼執行漏洞。
如果用戶可以控制字符串,則通過%n、%p等內容,最壞情況洗可以直接執行任意惡意代碼。

對數組delete時需要使用delete[]
這個沒處理好,它會引發低風險、中風險和高風險漏洞:內存泄漏、邏輯漏洞、內存破壞漏洞
delete []操作符用于刪除數組。delete操作符用于刪除非數組對象。它們分別調用operator delete[]和operator delete。
建議的解決方案在C++代碼中,使用string、vector、智能指針(比如std::unique_ptr)等可以消除絕大多數 delete[] 的使用場景,并且代碼更清晰。

使用switch中必須使用default
編程中switch沒應用好,它會引發兩大中風險漏洞:邏輯漏洞、內存泄漏漏洞。
switch中應該有default,以處理各種預期外的情況。這可以確保switch接受用戶輸入,或者后期在其他開發者修改函數后確保switch仍可以覆蓋到所有情況,并確保邏輯正常運行。

在debug版本或錯誤信息中不提供過多信息。
提供過多的信息,這會引發中風險的信息泄露漏洞。
包含過多信息的Debug消息不應當被用戶獲取到。Debug信息可能會泄露一些值,例如內存數據、內存地址等內容,這些內容可以幫助攻擊者在初步控制程序后,更容易地攻擊程序。
不能返回棧上變量的地址和使用未初始化棧變量
這個情況,會引發高風險的內存破壞漏洞。
函數不可以返回棧上的變量的地址,它的內容再函數返回后就會失效,可以用堆類傳遞簡單類型變量。
在棧上聲明的變量使用之前確認是否已經初始化了。最好是在聲明變量的時候,就直接初始化變量值。
建議方案:強烈建議返回 string、vector 等類型,會讓代碼更加簡單和安全。
錯誤的范例

正確的用法范例

函數的每個分支都應該有返回值
函數中的分支沒處理好,它會引發兩大中風險漏洞:信息泄露,邏輯漏洞。
函數的每個分支都應該有返回值,否則如果函數走到無返回值的分支,其結果是未知的。
錯誤用法的范例

正確用法的范例

在多線程中變量應確保線程安全性
線程中的變量沒處理好,它會引發兩大中風險漏洞:信息泄露,邏輯漏洞。
當一個變量可能被多個線程使用時,應當使用原子操作或加鎖操作。
建議解決方案:
對于C代碼,C11 后推薦使用 atomic 標準庫。
對于C++代碼,C++11后,推薦使用 std::atomic。
錯誤用法范例

正確用法范例

在程序中不得明文存儲敏感信息。
存儲明文信息,它會引發高風險漏洞風險:敏感信息泄露漏洞。
用戶的敏感信息應該使用加密算法進行做處理,并做到傳輸過程中加密,存儲過程中加密,存儲狀態下加密。在程序運行內存中的用戶敏感信息應該完全抹除。
使用rand()類函數應正確初始化
編程中rand函數沒有正確初始化,它會引發邏輯漏洞的高風險漏洞。
在編程中,rand類函數的隨機性并不高。而且在使用前需要使用srand()來初始化。未初始化的隨機數可能導致某些內容可預測。

操作文件時候避免路徑穿越問題
編程中,如果文件路徑沒處理好,它會引發高風險的邏輯漏洞。
在進行文件操作時,需要判斷外部傳入的文件名是否合法,如果文件名中包含 ../ 等特殊字符,則會造成路徑穿越,導致任意文件的讀寫。

避免相對路徑導致被劫持的問題
編程中相對路徑沒處理好會引發邏輯漏洞風險。
在編程中,使用相對路徑可能導致一些安全風險,例如DLL、EXE劫持等問題。
針對DLL劫持編碼安全的建議:
- 調用LoadLibrary,LoadLibraryEx,CreateProcess,ShellExecute等進行模塊加載的函數時,指明模塊的完整(全)路徑,禁止使用相對路徑,這樣就可避免從其它目錄加載DLL。
- 在應用程序的開頭調用SetDllDirectory(TEXT("")); 從而將當前目錄從DLL的搜索列表中刪除。
- 結合SetDefaultDllDirectories,AddDllDirectory,RemoveDllDirectory這幾個API配合使用,可以有效的規避DLL劫持問題。

文件權限控制
編程中,文件權限沒處理好,它會引發中風險的邏輯漏洞風險。
在創建文件時,需要根據文件的敏感級別設置不同的訪問權限,以防止敏感數據被其他惡意程序讀取或寫入。

防止整數溢出
在編程中,數據操作時候沒處理好,它會引發高風險的漏洞:內存破壞。
在計算時需要考慮整數溢出的可能,尤其在進行內存操作時,需要對分配、拷貝等大小進行合法校驗,防止整數溢出導致的漏洞。
錯誤用法范例

正確用法范例

防止Off-By-One漏洞
計算和操作數據的時候沒處理好,它會引發高風險漏洞:內存破壞
在進行計算或者操作時,如果使用的最大值或最小值不正確,使得該值比正確值多1或少1,可能導致安全風險。
解決方案:建議使用 string、vector 等組件代替原始指針和數組操作。

運算時檢查除以零異常
編程中,數據運算沒檢查除以零的情況,它會引發低風險的漏洞:拒絕服務漏洞。
在進行除法運算時,需要判斷被除數是否為零,以防導致程序不符合預期或者崩潰。

防止數字類型的錯誤強轉
在編程中數值類型沒處理好,它會引發中風險邏輯漏洞和高風險內存破壞漏洞。
在有符號和無符號數字參與的運算中,需要注意類型強轉,它可能導致的邏輯錯誤,建議指定參與計算時數字的類型或者統一類型參與計算。
下圖是參考范例

比較數據大小時加上最小或最大值的校驗
編程中數據比較沒處理好,它會引發高風險的內存破壞漏洞
在編程中進行數據大小比較時,要合理地校驗數據的區間范圍,建議根據數值類型,對其進行最大和最小值的判斷,以防止非預期錯誤。
????????????????????????????????????????????????????????????????
?????????????