本書是業界最廣泛采納的編程指導原則匯編 ,它緊扣各個版本的C語言標準,分門別類地介紹了各種可能引發可利用安全漏洞的未定義行為、未指定行為,提出了安全編碼的規則和建議,在每條規則和建議上都用現實的相容及不相容代碼示例加以說明,本書是該標準文檔的第2版,加入了對最新的C11標準的支持,對于所有有志于C語言軟件開發的技術人員來說,都是不可或缺的參考書。
全書共14章,包括98條編碼規則,每條規則都由一個標題、一段說明和不相容/相容的代碼示例組成。第1章講述與預處理器相關的規則;第2章介紹的規則與聲明和初始化相關;第3章介紹的是與表達式相關的規則;第4~7章講述的規則分別與整數、浮點數、數組及字符和字符串相關;第8章介紹與內存管理相關的規則;第9章講解的規則與輸入/輸出相關;第10章介紹的規則與環境相關;第11~13章分別講解與信號、錯誤處理和并發性有關的規則;第14章講述幾條雜項規則。最后提供3個附錄,分別包括本書使用的詞匯表、未定義行為和未指定行為。
C語言安全編程的難度可能超乎許多有經驗的程序員的想象。為了幫助編程人員編寫更安全的代碼,本書提供了CERT C安全編碼標準第二個正式發行版本的完整文檔。這個新版本中的規則有助于確保程序員的代碼完全遵循新的C11標準;它也處理早期版本(包括C99)的問題。
新標準列舉了當前C語言軟件漏洞的根源,按照嚴重性、利用的可能性和補救成本排定優先級。書中的98個指導方針都包含了不安全代碼和對應的C11相容安全實現。如果統一應用,這些指導方針將消除導致緩沖區溢出、格式字符串漏洞、整數溢出和其他常見漏洞的嚴重編碼錯誤。
Preface 前 言本書為C語言編碼提供了規則。這些規則的目標是開發安全、可靠和穩固的系統,例如,消除可能導致程序意外行為和可利用漏洞的未定義行為。遵循本標準定義的編碼規則是確保C語言開發的軟件系統安全、可靠、穩固的必要條件(但不是充分條件)。安全和穩固的設計也是必要的,安全性關鍵系統通常會提出比編碼標準更嚴格的要求,例如,要求所有內存都是靜態分配的。然而,應用本編碼標準將產生高質量的系統,這些系統可靠、健壯并且能夠抵御攻擊。
每條規則都由一個標題、一段說明和不相容/相容的代碼示例組成。標題是規則的簡潔描述,但是有時候不夠精確。說明提出了規則的規范要求。不相容代碼示例是違反規則的代碼示例。搭配的相容解決方案展示了等價的代碼,這些代碼不違反該規則或者該編碼標準中的任何其他規則。
具有良好文檔、可以實施的編碼標準是C語言編碼必不可少的要素。編碼標準鼓勵程序員遵循由項目需求和組織確定的一組統一規則,而不是簡單地采用程序員熟悉的方法。一旦確定,這些標準可以作為評估源代碼(使用人工或者自動化過程)的指標。
CERT編碼規則為業界廣泛采納。Cisco系統公司在2011年10月的Cisco年度SecCon會議上宣布在其產品開發中采用CERT C安全編碼標準作為基準編程標準。最近,Oracle將所有CERT安全編碼標準整合到現有的安全編碼標準中。注意,這是長期協作中的最新步驟:CERT和Oracle以前合作編寫了《The CERT Oracle Secure Coding Standard for Java》(Addison-Wesley,2011)。
Robert C. Seacord 是卡內基-梅隆大學軟件工程研究所(SEI)CERT計劃的安全編碼技術經理。CERT項目是運營相關網絡安全研究和對美國網絡安全挑戰創新/及時響應的可信提供商。安全編碼倡議和軟件開發人員及軟件開發組織合作,在部署之前消除由于編碼錯誤造成的安全漏洞。Robert還是卡內-基梅隆大學計算機科學學院和信息網絡學院的副教授。他曾經撰寫過8本書籍,包括《Secure Coding in C and C++》第2版和《Java Coding Guidelines: 75 Recommendations for Reliable and Secure Programs》等。他還發表過40篇軟件安全性、基于組件的軟件工程、基于Web系統設計、遺留系統現代化、組件儲存庫和搜索引擎以及用戶界面設計和開發方面的論文。
譯者序
前言
貢獻者簡介
第1章 預處理器(PRE)
1.1 PRE30-C. 不要通過連接創建通用字符名稱
1.2 PRE31-C. 避免不安全宏參數的副作用
1.3 PRE32-C. 不要在類函數的宏調用中使用預處理器指令
第2章 聲明和初始化(DCL)
2.1 DCL30-C. 聲明具有正確存儲持續期的對象
2.2 DCL31-C. 在使用前聲明標識符
2.3 DCL36-C. 不要聲明具有沖突鏈接類別的標識符
2.4 DCL37-C. 不要聲明或者定義保留標識符
2.5 DCL38-C. 使用正確語法聲明靈活數組成員
2.6 DCL39-C. 避免在結構填充中泄露信息
2.7 DCL40-C. 不要創建相同函數或者對象的不兼容聲明
2.8 DCL41-C. 不要在switch語句第一個條件標簽之前聲明變量
第3章 表達式(EXP)
3.1 EXP30-C. 不要依賴求值順序以避免副作用
3.2 EXP32-C. 不要通過非易失性引用訪問易失性對象
3.3 EXP33-C. 不要讀取未初始化的內存
3.4 EXP34-C. 不要對null指針進行解引用
3.5 EXP35-C. 不要修改具有臨時生命期的對象
3.6 EXP36-C. 不要將指針轉換為更嚴格對齊的指針類型
3.7 EXP37-C. 用正確數量和類型的參數調用函數
3.8 EXP39-C. 不要通過不兼容類型的指針訪問變量
3.9 EXP40-C. 不要修改常量對象
3.10 EXP42-C. 不要比較填充數據
3.11 EXP43-C. 使用restrict限定的指針時避免未定義行為
3.12 EXP44-C. 不要向sizeof、_Alignof或者_Generic傳遞有副作用的操作數
3.13 EXP45-C. 不要在選擇語句中執行賦值
第4章 整數(INT)
4.1 INT30-C. 確保無符號整數運算不產生回繞
4.2 INT31-C. 確保整數轉換不會造成數據丟失或者錯誤解釋
4.3 INT32-C. 確保有符號整數的運算不造成溢出
4.4 INT33-C. 確保除法和余數運算不會造成0除數錯誤
4.5 INT34-C. 不要用負數或者不小于操作數位數的位數對表達式進行移位
4.6 INT35-C. 使用正確的整數精度
4.7 INT36-C. 將指針轉換為整數或者將整數轉換為指針
第5章 浮點數(FLP)
5.1 FLP30-C. 不要使用浮點變量作為循環計數器
5.2 FLP32-C. 避免或者檢測數學函數中的定義域和值域錯誤
5.3 FLP34-C. 確保浮點數轉換在新類型的范圍內
5.4 FLP36-C. 將整數值轉換為浮點指針類型時保持精度
第6章 數組(ARR)
6.1 ARR30-C. 不要形成或者使用超限的指針或者數組下標
6.2 ARR32-C. 確保變長數組的大小參數在有效范圍內
6.3 ARR36-C. 不要進行兩個不引用相同數組的指針之間的減法或者比較
6.4 ARR37-C. 不要在指向非數組對象的指針上加或者減一個整數
6.5 ARR38-C. 保證庫函數不形成無效指針
6.6 ARR39-C. 不要在指針上加或者減一個按比例調整的整數
第7章 字符和字符串(STR)
7.1 STR30-C. 不要企圖修改字符串字面量
7.2 STR31-C. 保證字符串存儲有足夠的空間容納字符數據和null結束符
7.3 STR32-C. 不要向要求字符串參數的庫函數傳遞非null結束字符序列
7.4 STR34-C. 在轉換為更大的整數尺寸之前將字符轉換為unsigned char類型
7.5 STR37-C. 字符串處理函數的實參必須可以表示為unsigned char
7.6 STR38-C. 不要混淆窄和寬字符串及函數
第8章 內存管理(MEM)
8.1 MEM30-C. 不要訪問已釋放內存
8.2 MEM31-C. 在不再需要時釋放動態分配的內存
8.3 MEM33-C. 動態分配和復制包含靈活數組成員的結構
8.4 MEM34-C. 只釋放動態分配的內存
8.5 MEM35-C. 為對象分配足夠的內存
8.6 MEM36-C. 不要通過調用realloc()修改對象的對齊方式
第9章 輸入/輸出(FIO)
9.1 FIO30-C. 從格式字符串中排除用戶輸入
9.2 FIO31-C. 不要打開已經打開的文件
9.3 FIO32-C. 不要在只適合文件的設備上執行操作
9.4 FIO34-C. 區分從文件讀入的字符和EOF/WEOF
9.5 FIO37-C. 不要假定fgets()或者fgetws()在成功時返回非空字符串
9.6 FIO38-C. 不要復制FILE對象
9.7 FIO39-C. 不要在沒有中間刷新或者定位調用的情況下在一個流中交替輸入和輸出
9.8 FIO40-C. 在fgets()或者fgetws()失敗時重置字符串
9.9 FIO41-C. 不要用有副作用的流作為實參調用getc()、putc()、getwc()或者putwc()
9.10 FIO42-C. 在不再需要時關閉文件
9.11 FIO44-C. 對fsetpos()只使用fgetpos()返回的值
9.12 FIO45-C. 避免訪問文件時出現TOCTOU競爭條件
9.13 FIO46-C. 不要訪問已關閉文件
9.14 FIO47-C. 使用有效格式字符串