本书是一本重要的桌面参考手册,它记录了《CERT C安全编码标准》的第1次官方发布。这个标准逐项描述了C语言程序中导致软件潜在风险根源的编码错误,并根据严重性、被利用的可能性以及修补成本设置了优先级。每个指导方针提供了不安全代码的例子以及安全的替代方案。如果统一应用这些指导方针,它们可以消除可能导致缓冲区溢出、格式字符串潜在风险、整数溢出和常见的软件潜在风险的关键编码错误。
每一位C程序员的案头必备
实现C安全编程的权威指南
C安全编码标准
(美)Robert C. Seacord 著
徐 波 等译
“我是CERT安全编码活动的热情支持者。程序员可以找到关于正确性、清晰性、可维护性、性能等方面的很多建议,但对于特定的语言特性如何影响安全性方式的建议则明显缺乏。本书填补了这个空白。”
—— Randy Meyers,ANSI C主席
“在过去的几年里,我们依赖于CERT/CC公布各种建议,它们记录了无尽的安全问题。现在,CERT已经汇集了前沿技术专家的建议,向程序员和项目经理提供了实用的指导方针,避免在新的应用程序中出现这些问题,并帮助实现安全的遗留系统。干得好!”
—— Thomas Plum,Plum Hall,Inc.创始人
“互联网的存在极大地增加了对安全、防黑客应用程序的需要。通过组合这个CERT标准和其他安全指导方针,顾客可以获得全面的保护以及创建零缺陷软件的方法。”
—— Chris Tapp,领域应用程序工程师,LDRA Ltd.
“我觉得这个标准是无可代替的,它汇集了许多专家信息,让我们了解现代的软件系统在实践中是如何失败的。它是创建一个国际安全编码指导方针的良好起点。其他地方找不到这样的信息。就软件安全这个问题而言,你不知道的东西常常会给你带来伤害。”
—— John McDonald,《The Art of Software Security Assessment》的作者之一
软件安全性对于公司的运作和财富具有很大的影响,与个人的生活也息息相关。为了创建安全的软件,开发人员必须知道什么地方存在危险。C的安全编码要比许多经验丰富的程序员所想像的更为困难。
本书是一本重要的桌面参考手册,记录了《CERT C安全编码标准》的第一次官方发布。这个标准逐项描述了C语言程序中导致软件潜在风险根源的编码错误,并根据严重性、被利用的可能性以及修补成本设置了优先级。每个指导方针提供了不安全代码的例子以及安全的替代方案。如果统一应用这些指导方针,可以消除可能导致缓冲区溢出、格式字符串潜在风险、整数溢出和常见的软件潜在风险的关键编码错误。
作者简介
Robert C. Seacord 在位于宾夕法尼亚州匹兹堡市的软件工程协会(SEI)的CERT小组倡导了安全编码活动。他是《Secure Codeing in C and C++》(Addison-Wesley,2005)和另外两本书的作者。除了其他与安全相关的活动,CERT还定期分析软件潜在风险报告,并评估它们对Internet和其他关键的基础结构的风险。无数的专家为这个开放的开发过程作出了贡献,并对组成这个标准的规则和建议进行了审核。
在C编程语言中,安全编码的一个本质要素是具有良好文档的、可实行的编码标准。编码标准鼓励程序员遵循一组根据项目或组织的要求所确定的统一指导方针,而不是根据程序员的熟悉或偏好来作出决定。编码标准一经确立之后,就可以作为标尺,对源代码进行评估(使用手工或自动的过程)。
《CERT C安全编码标准》(The CERT C Secure Coding Standard)提供了在C编程语言中进行安全编码的指导方针。这些指导方针的目标是消除不安全的编码实践以及可能被利用而导致潜在风险的未定义行为。在开发代码时遵循这些标准将会产生高质量的软件系统,使它们具有更健壮的行为,对攻击的抵抗性也更强。
本标准受到软件工程协会(SEI)和其他得到许可的伙伴培训部门的支持,并可以作为全球信息保证认证(GIAC)安全软件程序员-C(GSSP-C)考试和认证的基础。
安全软件的需求
1988年11月发生的Morris蠕虫事件导致10%的Internet系统中止,并使人们对安全软件系统有了一个新的、准确的认识。20年后,许多安全分析师、软件开发人员、软件用户和策略制定者都在问同一个问题:“为什么软件不能变得更安全?”
首要的问题是“软件安全”这个术语用在当今已经没有意义。和其他人一样,我也试图定义这个术语,但是并不存在被广泛接受的定义。这是为什么呢?
人们已经提出了软件不能变得更安全的各种原因:工具不够充足、程序员缺乏足够的培训、开发周期太短等。但是,这些都是可以克服的问题。问题的根源在于其他方面。
软件不能变得更安全的原因是缺少对安全软件的需要。简单地说,如果一家开发商已经推出了一种功能更丰富、性能更出色的产品,而另一家开发商却提供了一种虽然安全但功能和性能稍差的产品,并且6个月之后才能上市。毫无疑问,顾客会购买第一种产品,生产商也深知这一点。
为什么顾客不愿购买安全产品呢?这是由于“安全”这个词在这种情况下是没有意义的。顾客为什么要放弃看得到的好处,而去购买一种定义不明确的、不可触摸的属性呢?
本编码标准就致力于解决这个问题。虽然在开发代码时遵循这个标准并不保证软件系统的安全性,但是它向我们提供了大量与代码的质量和安全有关的知识。它告诉我们在开发软件时应该遵循一组由该领域的前沿专家所开发的行业标准的规则和建议。它还告诉我们在开发软件时遵循这个标准可以使我们把注意力和精力集中在编写代码上,而不会受到一些常见的编码错误的困扰。在过去的20年里,CERT协作中心(CERT/CC)已经接到报告并发表了无数由于这些编码错误导致的潜在风险。它告诉我们生产代码的软件开发人员对违反这个标准可能导致并被利用的各种潜在风险具有深入的理解,因此在开发软件时头脑中已经形成了真正的安全思想。
因此,我们在本书中已经着手处理的一个“小”问题是改变开发和购买软件系统的市场动态。通过为C语言程序产生一个“可供行动参考的和可测量的”定义,即遵循这个标准中的规则和建议。我们定义了一种机制,顾客可以通过这种机制来要求安全的软件系统,而生产商也可以根据这种机制来满足顾客的要求。而且,由于“安全”这个词有了确切的含义,因此安全系统这个概念也开始变得有价值。
社区开发过程
《CERT C安全编码标准》的开发历时两年半的时间,是226位撰稿人和评论人共同努力的结晶。
这个标准的开发过程如下:
1)编码标准中的建议和规则来自致力于C编程语言开发和应用的社区,包括负责C标准和用户组的正式标准机构。
2)这些规则和建议的内容和风格由CERT技术部门和业界专家通过CERT安全编码标准wiki(www.securecodingcertorg)进行编辑。
3)用户社区通过穿插讨论和其他通信工具对公开张贴的内容进行审查和评论。这个标准的草案在London和Kona会议上由ISO/IEC WG14进行审阅,并接受公众(包括C和C++用户协会ACCU以及complangc新闻组)的审查。
Wiki和本书
在wiki上开发一个安全编码标准具有许多优势。但是,它的一个缺点是其内容不断发生变化。如果希望得到最新的信息或者有兴趣研究还没有完全通过审查的新变化,这当然是很理想的。但是,许多软件开发组织需要一个最终文档,然后它们才能致力于遵循一组(固定的)规则和建议。本书的目标就是作为《CERT C安全编码标准》的10版。
自2008年6月本书出版之后,10版就开始和安全编译标准的wiki版本出现差异。由于它们所讨论的都是C语言,并且我们对如何安全地使用C语言的认识仍在进步之中,所以CERT将继续在安全编码wiki上发展《CERT C安全编码标准》。这些变化可能会在将来融入到本标准的官方发布版本中。
目标
本书向开发人员提供了用C编程语言进行安全编码的指导方针。这些指导方针具有各种用途。首先,它们列举了在C语言编程中可能导致缺陷、漏洞和软件潜在风险的常见错误。对于这些错误,标准并没有要求遵循标准的编译器产生致命的诊断信息。换句话说,编译器常常在不发出任何警告的情况下产生可执行文件,并可以被部署和发行。最终的软件仍然可能包含缺陷,具有被攻击的潜在风险。
其次,这个编码标准提供了如何创建安全代码的建议。不遵循这些标准并不意味着软件一定是不安全的,但是如果遵循这些建议,它们可以成为强大的工具,帮助消除软件中的潜在风险。
再次,这个编码标准标识了不可移植的编码实践。移植性并不是安全的严格需求,但代码中不可移植的假设总是一个隐患,当代码移植到这些假设不再有效的平台时,就会导致潜在风险。
规则
本书的指导方针分为规则和建议两种。如果满足下面的条件,指导方针就会定义为规则:
1)违反编码实践有可能导致安全缺陷,产生可被利用的潜在风险。
2)违反这种编码实践仍然可以保证正确行为的情况是屈指可数的。
3)可以通过自动分析、形式方法或手工检查技巧检查是否遵循编码标准。
要保证用C编程语言所开发的软件系统的安全性,实现本标准所定义的安全编码规则是必需的(但并不充分)。图P1展示了这个安全编码标准的89个规则是如何分类的。
建议
如果满足下面的条件,指导方针就会定义为建议:
1)应用这种编码实践可能会提高系统的安全性。
2)由于未满足一个或多个编码实践的需求,而不能定义为规则。
特定的开发实践所采纳的建议数量取决于最终的软件产品的安全需求。具有很高安全需求的项目可能会重视安全方面的资源,因此所采纳的建议可能更多。
图P2展示了这个安全编码标准中的132个建议是如何分类的。
图P1CERT安全编码规则
为了保证源代码遵循这个安全编码标准,有必要采取措施检查违反规则的情况。实现这个目的的最有效方法就是使用一种或多种静态分析工具。如果工具无法检查某个规则,就需要采用手工检查的办法。
为了自动检测违反CERT C安全编码标准的规则和建议的情况,可以使用免费的和商业的源代码分析工具,包括Compass/ROSE,它是由Lawrence Livermore National Laboratoy开发的,并由CERT进行了扩展(wwwrosecompilerorg)。
范围
《CERT C安全编码标准》专门用于下面这些出版物所定义的C编程语言的各个版本:
ISO/IEC 9899:1999,Bogramming LanguagesC,第2版【ISO/IEC 9899:1999】
技术勘误TC1、TC2和TC3
ISO/IEC TR 247311,Extensions to the C Library,第1部分:边界检查接口【ISO/IEC TR 247311:2007】
ISO/IEC TR 247312,Extensions to the C Library,第2部分:动态分配函数【ISO/IEC TR 247312】
图P2 CERT安全编码建议
这个标准所包含的大多数材料也适用于早期版本的C编程语言。
这个标准所包含的规则和建议是为了实现代码的操作系统和平台独立性。但是,安全编码问题的最佳解决方案常常是平台特定的。在大多数情况下,这个标准为遵循POSIX的操作系统和Windows操作系统提供了顺应性解决方案。在许多情况下,也为像Linux和OpenBSD这样的特定平台提供了顺应性解决方案。偶尔,我们会在相关的场合指出平台特定的行为。
基本原则
C编程语言的安全编码标准专注于C99和相关的后C99技术报告,可以在最长时间内创造最大的价值。另外,由于开发新代码所耗费的金钱和努力远远多于维护现有的代码,最大的投资回报来自于开发新代码的有影响的程序员【Seacord 03】。但是,维护现有的代码仍然是一个重要的问题。
C标准(C99)尽可能地记录了现有的实践【ISO/IEC 9899:1999】。也就是说,大多数特性必须在一种实现中进行测试,然后才能添加到标准中。《CERT C安全编码标准》的目标不是这样。如果现有的实践符合它的目标,那当然很好。但是,它的目标是创建一组新的实践,包括引入一些尚未广为人知的概念。换句话说,CERT C安全编码指导方针试图驱使人们采用新的实践,而不仅仅是用文档记录它们。
例如,C函数库技术报告第1部分(TR 247311)正在日益壮大,但是目前只有少数厂商实现了它。它引入了像memcpy_s()这样的函数,这个函数通过在API中添加目标缓冲区的长度来实现安全目的。有远见的文档不会仅仅因为这些函数还没有广泛实现就忽略它们。
C99的实现较TR 247311更为普及。但是,即使在它尚未实现的场合,它仍然是业界前进的方向。尤其是开发新的C代码的开发人员,更希望能够最大限度地利用现在正在开发并对将来提供支持的编译器和其他工具。
有些厂商提供了C的扩展,还有些厂商在停止开发之前只实现了C标准的一部分。因此,不可能倒退回来,只讨论C90或C95。厂商对这个标准的支持非常复杂,很难简单地划一条线,表示一种特定的编译器正好支持标准的某一部分。不管选择的分界点是什么,站在对立面的厂商会抓住语言的不同部分各执一词。支持所有的可能性就需要在每个编译器上对每个语言特性都进行测试。因此,最近所选择的一个分界点是尽可能地使这个标准所定义的规则和建议可以应用。作为不同支持形式的结果,如果程序员只使用C90所指定的特性,就可以提高源代码的可移植性。这是C语言所固有的安全和移植性之间的许多权衡之一。
具有远见的信息的价值随着时间的增长会增加,而缺乏远见的信息的价值会很快变小。
由于这些原因,这个标准优先支持使用C99和后C99技术报告进行新代码的开发,另一个差不多的优先支持是使用C99和技术报告修正旧代码。
如果作用重大并且不会影响其他优化的目的,这个标准还会努力提供对旧式编译器的支持。它的意图并不是捕捉标准的所有偏离,只是捕捉其中一些较为重要的偏离。
未处理的问题
有一些问题是这个安全编码标准并没有解决的。
编码风格。编码风格问题是主观的,不可能在什么是最适当的编码风格上达成一致。因此这个标准并没有要求实行任何特定的编码风格,只要求用户定义编码风格的指导方法,并坚持使用这些指导方针。一致地使用一种编码风格最容易的方法是使用一种代码格式化工具。许多 交互性开发环境(IDE)提供了这样的功能。
工具。作为政府资助的研究和开发中心(FFRDC),SEI所处的位置并不是推荐特定的厂商和工具以实行这些指导方针。这个文档的用户可以自由地选择工具,并且鼓励厂商提供工具实行这些标准。
有争议的规则。一般而言,CERT安全编码标准试图避免包含缺乏广泛一致的具有争议的规则。
本书适合的读者
本书主要适用于C语言程序的开发人员。虽然安全对于面向Internet的系统非常重要,但是对于那些可能被包含或部署在安全软件系统中的任何软件组件,安全也是非常重要的。由于越来越多的软件系统是由软件组件所组成的,甚至是由其他软件系统组成的,因此很难保证软件不在需要安全的环境中使用,甚至是在安全要求极为严格的环境中使用。
本书还适用于那些没有意识到安全问题的C语言程序员,因为这些指导方针对于实现其他质量属性(如安全、可靠、健壮、可用和可维护性)也有很大的实用价值。
虽然本书并不面向C++程序员,但是由于C语言程序所涉及的许多问题在C++程序中也存在,因此,尽管在许多情况下它们的解决方案是不同的,但是本书对C++编程也有一定的参考价值。
本书的组织形式
本书包括一个介绍性章节,13个包含了特定主题领域的指导方针的章节,一个包含了POSIX指导方针的附录,演示了这个安全编码标准如何针对一个特定平台进行自定义。POSIX附录是非正式的,并不是这个标准的规定部分。
大多数指导方针具有一致的结构。这个标准中的每个指导方针都具有惟一的标识符,包含在标题中。指导方针的标题和介绍段落定义了规则或建议,接下来,一般是一对或多对非顺应性代码例子和顺应性解决方案。每个指导方针还包括了风险评估和参考资料。指导方针有可能包含一张相关潜在风险的表格。
指导方针标识符
指导方针标识符由3个部分组成:
由3个字母组成的助记符,表示标准中的节。
范围在00~99之间的一个两位数。
字母C表示这是C语言指导方针。
三字母助记符可以用于对相似的指导方针进行分组,并提示一个指导方针属于哪个分类。
两位数用于为每个指导方针提供一个惟一的标识符。范围在00~29的数值为建议而保留,范围在30~99的数值为规则而保留。
非顺应性代码例子和顺应性解决方案
非顺应性代码例子是违背了当前所讨论的指导方针的不安全代码例子。注意,这些只是例子,消除这些例子并不意味着代码已经遵循了当前的指导方针。
在非顺应性代码例子之后一般是顺应性解决方案,它显示了非顺应性代码例子可以怎样以安全、顺应的方式重新实现。除非另有说明,非顺应性代码例子应该只包含对当前所讨论规则的违反。顺应性解决方案应该遵循所有的安全编码规则,但偶尔可能无法遵循某个建议。
风险评估
每个指导方针都包含了一个风险评估小节,它试图对违反每个指导方针的风险都进行量化和质化。这个信息主要用于在修补项目时帮助确认修补的优先级,并假设新代码的开发完全遵循整个标准。
每个规则和建议都具有一个优先级。优先级是根据失败模式、效果和危急分析(FMECA)进行度量的【IEC/ 60812】。每个规则分别设置3个1~3之间的值。
严重性:忽略这个规则后果的严重程度。
1:低(拒绝服务攻击、异常终止)。
2:中等(数据完整性被破坏、违背意图的信息泄露)。
3:高(运行任意代码)。
可能性:忽略这个规则所产生的缺陷导致可被利用的潜在风险的可能性。
1:不太可能。
2:有可能。
3:很可能。
修补成本:遵循这个规则的代价有多大。
1:高(手工检测并修正)。
2:中等(自动检查和手工修正)。
3:低(自动检测并修正)。
每个规则的这3个值相乘产生一个度量,可以作为应用这个规则的优先级。乘积的范围是1~27。优先级在1~4范围的规则和建议属于第3级别的规则,6~9属于第2级别的规则,12~27属于第1级别的规则。因此,可能通过实现某一个级别的所有规则,声明达到对一个标准的第1级顺应性、第2级顺应性和完全顺应性(级别3),如图P3所示。
建议并不是强迫的,提供风险评估只是作为参考。
图P3优先级和级别
参考资料
指导方针经常包含了对CERT/CC潜在风险备注数据库的潜在风险说明【CERT/CC VND】、MITRE的常见脆弱列举(CWE)中的CWE ID【MITRE 07】和来自MITRE的常见潜在风险和暴露的CVE编号的引用。
读者可以创建一个惟一的URL,通过在一个固定的字符串末尾添加一个相关的ID,获取与任何主题有关的信息。例如:
为了获取与VU#551436“Moxilla Firefox SVG浏览器的整数溢出潜在风险”有关的详细信息,可以在https://wwwkbcert/org/vulnotes/id/后面添加551436,并在浏览器中输入结果URL:https://wwwkbcert/org/vulnotes/id/551436。
为了获取与CWE ID 192“Integer Coercion Error”有关的详细信息,可以在http://cwemitre org/data/definitions/后面添加192html,并在浏览器中输入结果URL:http://cwemitreorg/data/definitions/192html。
为了获取与CVE-20061174有关的详细信息,可以在http://cvemitreorg/cgi_bin/ cvename cgi name=后面添加CVE20061174,并且在浏览器中输入结果URL:http://cvemitreorg/cgi_bin/cvenamecgi name=CVE20061174。
指导方针经常与“信息技术—编程语言—通过语言的选择和使用避免编程语言中的潜在风险”中的语言潜在风险相关【ISO/IEC PDTR 24772】。
相关潜在风险
与违背CERT/CC潜在风险备注数据库所公布的实际潜在风险相联系的规则和建议具有“相关潜在风险”这个小节,以表格形式出现,如下例子所示:
度量ID公布日期名称
162VU#6067002007年3月19日文件整数溢出潜在风险
206VU#5594442007年3月13日Apple Mac OS X ImageIO整数溢出潜在风险
2222VU#5514362007年2月23日Mozilla Fireofx SVG浏览器存在整数溢出潜在风险
0VU#1622892006年4月17日C编译器可能会安静地丢弃一些回绕检查
新的链接不断地添加。最新的相关潜在风险的列表可在以下网址找到:
https//wwwkbcertorg/vulnotes/bymetric searchview&query=FIELD+KEYWORDS+contains+XXXNNX
其中,XXXNNX是所搜索的规则或建议的ID。
这种表格由4个字段组成:度量、ID、公布日期和名称。
潜在风险的度量。CERT潜在风险的度量值是一个0~180之间的整数,近似地模拟了这个潜在风险的严重性。这个值合并了7个要素:
与这个潜在风险有关的信息是否广泛可知。
是不是向CERT或其他事件响应小组报告了由于这个潜在风险所导致的事件。
由于这个潜在风险的存在,是否对Internet基础结构(例如路由器、名称服务器、关键的Internet协议)产生了风险。
Internet上的多少系统受这个潜在风险的影响。
利用了这个潜在风险的后果有多严重。
这个潜在风险是不是很容易利用。
利用这个潜在风险是不是需要前提条件。
由于对这些问题的回答是根据潜在风险分析师的判断所产生的近似值,在不同的网站可能有明显的区别,因此不应该过度依赖潜在风险的度量来确定它们的响应优先级。度量值的主要作用是把严重的潜在风险从数据库中大量的不严重的潜在风险中分离出来。由于这些问题的权重不同,因此结果也不是线性分布的(也就是说,度量为40的潜在风险的严重性并不是度量为20的潜在风险的严重性的两倍)。
另一种潜在风险严重程序度量是公用潜在风险评分系统(CVSS)【Mell 07】。
潜在风险ID。潜在风险ID号是随机赋值的,是为了惟一地标识一个潜在风险。这些ID为4~6位数字,通常具有VU#前缀,表示它们是潜在风险ID。
公布日期。这是这个潜在风险被公开发布的日期。通常这个日期是在第1次公布这个潜在风险说明时、第1次被发现这个潜在风险的利用时、厂商第1次发布相关的补丁或者这个潜在风险的描述被贴到一个公开的邮件列表时。在默认情况下,这个日期被设置为这个潜在风险说明的公布日期。
潜在风险的名称。潜在风险的名称是一个简单的描述,对问题的本质以及所影响的软件产品进行了总结。虽然这个名称可能包含了一个条款,描述了这个潜在风险的后果,但大多数名称专注于导致问题发生的缺陷本质。
程序设计
“我是CERT安全编码活动的热情支持者。程序员对于正确性、清晰性、可维护性、性能等方面的建议具有很多需求,但对于特定的语言特性如何影响安全性方式的建议则明显缺乏。《CERT? C安全编码标准》填补了这个空白。”
——Randy Meyers,ANSI C主席
“在过去的几年里,我们依赖CERT/CC公布各种建议,它们记录了无尽的安全问题。现在,CERT已经汇集了前沿技术专家的建议,向程序员和项目经理提供了实用的指导方针,避免在新的应用程序中出现这些问题,并帮助实现安全的遗留系统。干得好!”
——Thomas Plum,Plum Hall,Inc.的创始人
“互联网的存在极大地增加了对安全、防黑客应用程序的需要。通过组合这个CERT标准和其它安全指导方针,顾客可以获得全面的保护以及创建零缺陷软件的方法。”
——Chris Tapp,领域应用程序工程师,LDRA Ltd.
“我觉得这个标准是无可代替的,它汇集了许多专家信息,让我们了解现代的软件系统在实践中是如何失败的。它是创建一个国际安全编码指导方针的良好起点。其它地方找不到这样的信息。就软件安全这个问题而言,你不知道的东西常常会给你带来伤害。”
——John McDonald,《软件安全评估艺术》的共同作者
软件安全性对于公司的运作和财富具有很大的影响,对于个人的生活也是息息相关。为了创建安全的软件,开发人员必须知道什么地方存在危险。C的安全编码要比许多经验丰富的程序员所想像的更为困难。
本书是一本重要的桌面参考手册,它记录了《CERT? C安全编码标准》的第1次官方发布。这个标准逐项描述了C语言程序中导致软件潜在风险根源的编码错误,并根据严重性、被利用的可能性以及修补成本设置了优先级。每个指导方针提供了不安全代码的例子以及安全的替代方案。如果统一应用这些指导方针,它们可以消除可能导致缓冲区溢出、格式字符串潜在风险、整数溢出和常见的软件潜在风险的关键编码错误。
Robert C. Seacord在位于宾夕法尼亚州匹兹堡市的软件工程协会(SEI)的CERT小组倡导了安全编码活动。他是《Secure Codeing in C and C++》(Addison-Wesley,2005)和另外两本书的作者。CERT以及其它与安全相关的活动定期分析软件潜在风险报告,并评估它们对Internet和其它关键的基础结构的风险。无数的专家为这个开放的开发过程作出了贡献,并对组成这个标准的规则和建议进行了审核。
(美)Robert C. Seacord:暂无简介
徐波 等译:暂无简介
关于作者
前言
第1章本标准使用说明系统质量
自动生成的代码
顺应性
第2章预处理器(PRE)
建议和规则
风险评估汇总
相关规则和建议
PRE00C用内联函数或静态函数代替与函数相似的宏
PRE01C在宏参数名两边加上括号
PRE02C宏替换列表应该加上括号
PRE03C应该使用typedef定义编码类型
PRE04C不要复用标准头文件名
PRE05C理解连接标记或执行字符串化时的宏替换
PRE06C把头文件放在包含防护条件中
PRE07C避免使用连续的问号
PRE08C保证头文件名惟一
PRE09C不要用不安全的函数替换安全函数
PRE10C在一个dowhile循环中包装多条语句的宏
PRE30C不要通过连接创建统一字符名称
PRE31C不要在不安全宏的参数中包含赋值、增值、减值、volatile访问或函数调用
第3章声明和初始化(DCL)
建议和规则
风险评估汇总
DCL00C 用const限定不可修改的对象
DCL01C不要在子作用域中复用变量名
DCL02C使用视觉区别明显的标识符
DCL03C使用静态断言测试常量表达式的值
DCL04C不要在一个声明中声明超过1个的变量
DCL05C使用typedef声明提高代码的可读性
DCL06C使用有意义的符号常量表示程序逻辑中的字面值
DCL07C在函数声明器中包含适当的类型信息
DCL08C在常量定义中对关系进行正确的编码
DCL09C把返回errno错误代码的函数的返回类型声明为errno_t
DCL10C维护变参函数的编写者和调用者之间的契约
DCL11C理解与变参函数相关联的类型问题
DCL12C使用不透明类型实现抽象数据类型
DCL13C把不会被函数修改的值指针参数声明为const
DCL14不要对跨翻译单元的全局变量的初始化顺序作出假设
DCL15C把不需要外部链接的对象声明为static
DCL30C声明具有正确存储持久期的对象
DCL31C在使用标识符之前声明它们
DCL32C保证相互可见的标识符是惟一的
DCL33C保证函数实参中具有限制性限定的源指针和目标指针不引用重叠的对象
DCL34C对无法缓存的数据使用volatile
DCL35C不要使用与函数定义不匹配的类型转换函数
DCL36C不要声明具有冲突链接属性的标识符
第4章表达式(EXP)
建议和规则
风险评估汇总
相关的规则和建议
EXP00C使用括号保证操作的优先级
EXP01C不要用指针的长度确定它所指向类型的长度
EXP02C注意逻辑AND和OR操作符的短路行为
EXP03C不要认为结构的长度等于它的各个成员的长度之和
EXP04C不要在结构之间执行逐字节的比较
EXP05C不要转换掉const限定
EXP06C sizeof操作符的操作数不应该包含副作用
EXP07C不要在表达式中对常量的值作出假设而削弱常量的优点
EXP08C确保正确地使用指针运算
EXP09C使用sizeof确定类型或变量的长度
EXP10C不要依赖子表达式的求值顺序或副作用的发生顺序
EXP11C不要把期待一种类型的操作符应用于一种不兼容的类型
EXP12C不要忽略函数的返回值
EXP30C不要依赖序列点之间的求值顺序
EXP31C避免断言的副作用
EXP32C不要转换掉volatile限定
EXP33C不要引用未初始化的内存
EXP34C保证不对null指针进行解引用
EXP35C不要在后续的序列点之后访问或修改一个函数的调用结果中的数组
EXP36C不要把指针转换为对齐要求更严格的指针类型
EXP37C调用函数时使用API所指定的参数
EXP38C不要在位段成员或非法类型上调用offsetof()
第5章整数(INT)
建议和规则
风险评估汇总
相关的规则和建议
INT00C理解编译器所使用的数据模型
INT01C使用rsize_t或size_t类型表示所有表示对象长度的整数值
INT02C理解整数转换规则
INT03C使用安全的整数库
INT04C对来自不信任来源的整数值实行限制
INT05C如果输入函数无法处理所有可能出现的错误就不要用它们转换字符数据
INT06C使用strtol()或相关函数把字符串标记转换为整数
INT07C只使用显式的有符号或无符号char类型表示数值
INT08C验证所有的整数值位于范围内
INT09C保证枚举常量映射到惟一数值
INT10C使用%操作符时不要假设余数总是正的
INT11C把指针转换为整数或者把整数转换为指针时需要小心
INT12C当普通的整数位段用于表达式时,不要对它的类型作出假设
INT13C只对无符号操作数使用位操作符
INT14C避免在同一个数据上执行位操作和算术运算
INT15C在程序员定义的整数类型的格式化I/O中使用intmax_t或uintmax_t
INT30C保证无符号整数运算不产生回绕
INT31C保证整型转换不会丢失或错误解释数据
INT32C保证有符号整数运算不会产生溢出
INT33C保证除法和求模运算不会导致除零错误
INT34C移位的数量不能是负数或大于操作数的位数
INT35C把整型表达式比较或赋值为一种较大类型之前用这种较大类型对它进行求值
第6章浮点数(FCP)
建议和规则
风险评估汇总
相关规则和建议
FLP00C理解浮点数的限制
FLP01C在重新排列浮点表达式时需要注意
FLP02C需要精确计算时避免使用浮点数
FLP03C检测和处理浮点错误
FLP30C不要使用浮点数作为循环计数器
FLP31C不要用复数调用期望接受实数的函数
FLP32C防止或检测数学函数中的定义域错误和值域错误
FLP33C执行浮点运算时把整数转换为浮点数
FLP34C保证浮点转换位于新类型的范围之内
第7章数组(ARR)
建议和规则
风险评估汇总
相关规则和建议
ARR00C理解数组的工作方式
ARR01C获取数组的长度时不要对指针应用sizeof操作符
ARR02C显式地指定数组的边界,即使它已经由初始化值列表隐式地指定
ARR30C保证数组索引位于合法的范围之内
ARR31C在所有源文件中使用一致的数组记法
ARR32C保证变长数组的长度参数位于合法范围之内
ARR33C保证复制的目标具有足够的存储空间
ARR34C保证表达式中的数组类型是兼容的
ARR35C不允许循环迭代到数组尾部之后
ARR36C不要对两个并不指向同一个数组的指针进行相减或比较
ARR37C不要把一个指向非数组对象的指针加上或减去一个整数
ARR38C如果结果值并不引用合法的数组元素,不要把指针加上或减去一个整数
第8章字符和字符串(STR)
建议和规则
风险评估汇总
相关规则和建议
STR00C使用适合的类型表示字符
STR01C采纳和实现一致的字符串管理计划
STR02C对传递给复杂子系统的字符串数据进行净化
STR03C不要意外地截断null结尾的字节字符串
STR04C使用普通char类型表示基本字符集中的字符
STR05C引用字符串常量时使用const指针
STR06C不要以为strtok()会使解析的字符串不被修改
STR07C使用TR 24731修正现在的字符串操纵代码
STR08C使用托管字符串开发新的字符串操纵代码
STR30C不要试图修改字符串常量
STR31C保证字符串的存储具有足够的空间容纳字符数据和null结尾符
STR32C根据需要将字符串用null结尾
STR33C正确地判断宽字符串的长度
STR34C在转换为更大的整型长度时把字符转换为无符号类型
STR35C不要把未检查边界来源的数据复制到固定长度的数组
STR36C不要指定用字符串常量初始化的字符数组的边界
STR37C字符处理函数的参数必须能够用unsigned char表示
第9章内存管理(MEM)
建议和规则
风险评估汇总
相关规则和建议
MEM00C在同一个模块、同一个抽象层中分配和释放内存
MEM01C在free()之后立即在指针中存储一个新值
MEM02C把内存分配函数的调用结果立即转换为指向被分配类型的指针
MEM03C及时清除存储在可复用资源中的敏感信息
MEM04C不要执行零长度的分配
MEM05C避免大型的堆栈分配
MEM06C保证敏感数据不会被写入到磁盘
MEM07C保证calloc()的参数相乘后可以用size_t表示
MEM08C只把realloc()用于改变动态分配数组的大小
MEM09C不要假设内存分配函数会对内存进行初始化
MEM10C定义和使用指针验证函数
MEM30C不要访问已经被释放的内存
MEM31C动态分配的内存只应释放一次
MEM32C检测和处理内存分配错误
MEM33C使用正确的语法表示灵活数组成员
MEM34C只释放动态分配的内存
MEM35C为对象分配足够的内存
第10章输入/输出(FLO)
建议和规则
风险评估汇总
相关规则和建议
FIO00C在创建格式字符串时应该小心
FIO01C调用通过文件名标识文件的函数时必须小心
FIO02C对来自不信任来源的路径名进行标准化
FIO03C不要对fopen()和文件的创建作出假设
FIO04C检测和处理输入和输出错误
FIO05C使用多个文件属性标识文件
FIO06C创建具有正确访问权限的文件
FIO07C用fseek()代替rewind()
FIO08C在打开的文件上调用remove()时应该小心
FIO09C跨系统传输二进制数据时应该小心
FIO10C使用rename()函数时应该小心
FIO11C指定fopen()的mode参数时应该小心
FIO12C使用setvbuf()代替setbuf()
FIO13C不要压回多于1个的字符
FIO14C理解文件流的文本模式和二进制模式的区别
FIO15C保证文件操作在安全目录中执行
FIO16C通过创建jail限制对文件的访问
FIO30C排除格式字符串中的用户输入
FIO31C不要打开已经被打开的文件
FIO32C不要在专用于文件的设备上执行操作
FIO33C检测和处理导致未定义行为的输入输出错误
FIO34C使用int捕捉字符I/O函数的返回值
FIO35C当sizeof(int) == sizeof(char)时使用feof()和ferror()检测文件尾和文件错误
FIO36C不要假设fgets()会读取换行符
FIO37C不要假设被读取的是字符数据
FIO38C不要使用FILE对象的拷贝进行输入和输出
FIO39C不要在没有干预刷新或定位调用的情况下在一个流中交替地执行输入和输出
FIO40C在fgets()失败时重置字符串
FIO41C调用getc()或putc()时不要使用具有副作用的流参数
FIO42C保证当文件不再需要时及时将它们关闭
FIO43C不要在共享目录中创建临时文件
FIO44C只在fsetpos()中使用fgetpos()所返回的值
第11章环境(ENV)
建议和规则
风险评估汇总
相关规则和建议
ENV00C不要存储指向getenv()返回的字符串的指针
ENV01C不要对环境变量的长度作出假设
ENV02C注意具有相同有效名称的多个环境变量
ENV03C 调用外部程序时对环境进行净化
ENV04C如果不需要命令处理器就不要调用system()
ENV30C不要修改getenv()所返回的字符串
ENV31C在可能无效化环境指针的操作之后不能再依赖它
ENV32C所有的atexit处理函数都不能以除了正常返回之外的其他任何方式终止
第12章信号(SIG)
建议和规则
风险评估汇总
相关规则和建议
SIG00C屏蔽由不可中断的信号处理函数所处理的信号
SIG01C理解与信号处理函数的持久性有关的平台特定的细节
SIG02C避免使用信号实现常规的功能
SIG30C只在信号处理函数中调用异步安全的函数
SIG31C不要访问和修改信号处理函数中的共享对象
SIG32C不要在信号处理函数中调用longjmp()
SIG33C不要递归地调用raise()函数
SIG34C不要在不可中断的信号处理函数内部调用signal()
第13章错误处理(ERR)
建议和规则
风险评估汇总
相关规则和建议
ERR00C采用和实现一致的、全面的错误处理策略
ERR01C使用ferror()而不是errno检查FILE流错误
ERR02C避免带内错误指示符
ERR03C调用TR24731-1所定义的函数时使用运行时约束处理函数
ERR04C选择一种适当的终止策略
ERR05C独立于应用程序的代码应该在不提示错误处理的情况下提供错误检测
ERR06C理解assert()和abort()的终止行为
ERR30C调用设置errno的库函数之前把errno设置为0,并且在函数返回一个提示失败的值之后检查errno
ERR31C不要重定义errno
ERR32C不要依赖errno的不确定值
第14章其他(MSC)
建议和规则
风险评估总结
MSC00C在高警告级别进行干净的编译
MSC01C实现逻辑完整性
MSC02C避免因为省略所导致的错误
MSC03C避免因为多余所导致的错误
MSC04C用一种可读的风格一致地使用注释
MSC05C不要直接维护time_t类型的值
MSC06C处理敏感数据时注意编译器的优化
MSC07C检测和删除死代码
MSC08C库函数应该对形参进行验证
MSC09C字符编码:使用ASCII的子集以保证安全
MSC10C字符编码:UTF8相关的问题
MSC11C使用断言进行诊断测试
MSC12C检测和删除没有效果的代码
MSC13C检测和删除未使用的值
MSC14C不要引入不必要的平台依赖性
MSC15C不要依赖未定义的行为
MSC30C不要使用rand()函数产生伪随机数
MSC31C保证返回值与适当的类型进行比较
附录POSIX(POS)
建议和规则
风险评估汇总
相关的规则和建议
POS00C避免多线程的竞争条件
POS01C检查链接是否存在
POS02C遵循最小特权原则
POS30C正确地使用readlink()函数
POS31C不要解锁或销毁另一个线程的mutex
POS32C在多线程环境中使用位段时包含一个mutex
POS33C不要使用vfork()
POS34C不要用一个指向自动变量的指针为参数调用putenv()
POS35C避免检查符号链接是否存在时的竞争条件
POS36C在撤消特权时注意正确的撤消顺序
POS37C保证特权的撤消是成功的词汇表
参考资料