随着Java语言成为构建应用软件系统的主要开发环境和语言之一,对于安全性的关注也开始成为一个热点话题。本书就是为了帮助开发人员增加对Java语言安全性的认识而推出的技术专著。本书结合Java应用的开发和Java语言的使用,系统地阐述了使用Java语言时的相关安全规则。本书内容非常全面,包括基于Java标准版6.0平台(Java SE 6)环境中一系列应用于Java语言和类库的安全编码规则,并且对这一系列规则进行了分类,包括输入数据验证、声明和初始化、表达式、数值类型和操作、面向对象、方法使用、异常处理、可见性和原子性、锁、线程、输入输出、序列化、平台安全特性、Java运行环境等各个基本的分类部分。读者可以将本书作为Java安全方面的工具书,根据自己的需要,找到自己感兴趣的规则进行阅读和理解,或者在开发工作和软件设计中,遇到安全问题时,根据书中列出的大致分类对规则进行索引和阅读使用,另外,也可以通读全书的所有规则,系统地了解Java安全规则,增强对Java安全特性、语言使用、运行环境特性的理解。在国内的技术书籍中,像本书这样系统阐述Java安全的书籍并不多见,因此本书值得对Java安全感兴趣的读者系统阅读。
在Java的世界里,安全性并没有被视为一个附加功能。尽管这是一种普遍的思维方式,但如果不结合安全性去考虑问题,还是会陷入麻烦之中。但并不能因为有了基础设施的帮助,我们就可以想当然地认为安全性已经自动考虑在内了。近些年来,业界发展出一系列的安全标准和最佳实践。这本书就是这些实践的经验总结。它们并不是理论研究论文或者说市场营销的噱头,它们都是在重要项目中经过严格监测并且可以在企业级规模应用的产物。
—— James A. Gosling, Java语言之父
使用Java语言安全编码的一个关键组成部分是它具有良好归档和可直接使用的编码标准。编码标准鼓励开发人员遵从一个统一的规则集合,而这个规则集合往往是根据项目或者组织的需求来确定的,而不是根据开发人员自身的熟悉和喜好来制定的。一旦建立了标准,这些标准可以用于评估源代码的度量(通过手动或者自动的过程)。
本书提供了一系列的规则,这些规则可以用于消除那些不安全编码实践,这些实践可能会造成软件出现安全漏洞。通过运用规则可以让开发人员设计出质量更高的系统,从而对抗更多的攻击。这些规则可以应用于使用Java进行开发的很多产品,如PC、游戏机、移动电话、家用电器以及自动化电子设备等。
本书首先从总体上概述了Java应用安全,然后通过17个组织形式一致的章节来对各个关键领域的特定详细规则进行阐述。在每个领域中,作者展示了不符合规则的代码示例和相对应的解决方案,展示了如何评估风险,并且提供了可参考的延伸阅读文献。每章中的每一条规则都是根据产生结果的严重程度、引入安全漏洞的可能性,以及弥补代价的大小进行排序的。
本书提供了针对Java SE 6平台的安全编码规范,它包括Java编程语言和相关的类库,并且对Java SE 7平台中的新特性进行了讨论。本书通过描述受JVM和编译器实现所影响的语言行为,指导开发人员正确使用Java API和安全架构,以及在使用标准API扩展的时候考虑安全性问题。
作者简介
Fred Long 英国Aberystwyth大学计算机科学系高级讲师和教学主任。主要讲授形式方法、Java、C++和C的编程模式以及与编程相关的安全问题的课程。他是英国计算机协会中威尔士分会的主席,自1992年以来在软件工程研究所(SEI)担任客座研究员。最近正在研究如何在Java中探查安全性漏洞。
Dhruv Mohindra 印度Persistent系统工程有限公司的高级软件工程师。曾研发了广泛应用于企业服务器的监控软件。曾在SEI的CERT项目工作,并致力于在编程社区中提高对安全问题的警觉性。曾任职于卡内基·梅隆大学,拥有信息安全策略与管理硕士学位和印度Pune大学计算机工程学士学位。
Robert C. Seacord 资深计算机安全专家和作家。在计算机安全、历史系统改造以及基于组件的软件工程等领域具有极深的造诣。目前管理卡内基·梅隆大学SEI的CERT在安全编码领域的创新项目。拥有Rensselaer Polytechnic学院计算机科学学士学位。
Dean F. Sutherland CERT高级软件安全工程师,编译器后端技术专家组高级专家。拥有卡内基·梅隆大学博士学位。曾担任职业软件工程师,在Tartan公司工作超过14年。
David Svoboda CERT软件安全工程师,资深Java开发工程师,在Java开发领域拥有13年的开发经验。是卡内基·梅隆大学的一系列软件开发项目的主要开发者,这些项目涉及从层级芯片建模到社会组织仿真再到自动机器学习等多个方面。
在Java编程语言中,关键的安全编码要素是采用良好的文档和强制的编码规范。本书提供了在Java语言中的一系列安全编码规则。这些规则的目标是消除不安全的编码实践,因为这些不安全的因素会导致可利用的漏洞。如果应用这些安全编码标准,可以帮助设计出安全的、可靠的、健壮的、有弹性的、可用性和可维护性高的高质量系统,并且这些安全编码规范还可作为评估源代码质量特性的一个指标(不管使用的是手动的还是自动化的过程)。
对于使用Java编程语言开发的软件系统,这个编码规范具有广泛的影响。
本书范围
本书主要关注Java标准版6.0平台(Java SE 6)环境,其中包含一系列应用于Java编程语言和类库的安全编码标准。在《Java语言规范(第3版)》(The Java Language Specification, 3rd edition, JLS 2005)对Java编程语言的行为进行了描述,该规范主要是开发本标准的主要参考资料。本标准同样涉及Java SE 7平台中的新特性。这些新特性主要为解决存在于Java SE 6和Java SE 7平台的安全问题提供了替代性的兼容解决方案。
C和C++语言允许一些不确定、未指定或者在实现时才确定的行为,当程序员错误假设使用的API和开发语言的内在行为时,它们都会导致安全漏洞。《Java语言规范》进一步规范了标准化需求,这是因为Java被设计成一个“一次开发,到处运行”的语言。甚至Java虚拟机(JVM)或者Java编译器的一些行为可以由实现者自行决定。针对这些语言的特殊性,本标准也提出了安全的编码指导,以避免这些特殊性带来的问题。
仅仅关注于语言本身并不能编写出安全的软件。在Java应用编程接口(API)中存在的设计缺陷,在某些时候会导致这些API过时。在另外一些时候,这些API或者相关的文档还可能会被开发社区错误地解读。本标准指出了这些有问题的API并且强调了它们的正确使用方法,同样也包括那些广泛使用的错误设计模式(反模式)和使用习惯的例子。
Java语言的核心和扩展API以及JVM提供的安全特性包括安全管理器、访问控制器、加密、自动内存管理、强类型检查、字节码验证等。虽然这些安全特性为大部分的应用提供了足够的安全性保证,但是对这些特性的正确使用也是非常重要的。本标准突出与设计安全架构相关的隐患和注意事项,并强调其正确的实施。坚持这个标准可以保障受信任程序的保密性、完整性和可用性(Confidentiality, Integrity,and Availability,CIA),并且有助于消除安全漏洞,这些安全漏洞会导致拒绝服务攻击、time-of-check-to-time-of-use攻击、信息泄露、差错计算以及权限升级这些问题。
若软件遵循这些标准,则其使用者有能力定义细粒度的安全策略,并且在不受信的系统中执行受信的移动代码,或者在受信的系统中执行非受信的移动代码。
本书包含的类库
本书讨论的安全问题主要应用于lang和util类库,同时涉及Collections、并发包、Logging、Management、反射、正则表达式、Zip、I/O、JMX、JNI、Math、Serialization以及XML JAXP等类库。本标准不涉及已经发现的缺陷,不管这些缺陷已经得到修复还是因为设计本身就缺少安全性的考虑。本标准也包括功能缺陷,但只有这个缺陷发生频率高,造成相当大的安全问题,或它影响了基于核心平台的所有Java技术,它才会被考虑进来。本标准不仅涉及核心API的安全标准,而且包括一系列重要的安全考虑,涉及标准的扩展API(javax包)。
本书不会涉及的问题
本标准不会涉及以下问题:
设计和架构。本标准假设产品的设计和架构是安全的,也就是说,产品有足够的设计水平,而不致产生危及其安全的漏洞。
内容。本标准不关注那些在某种Java平台上出现的特殊问题,而关注广泛出现在所有平台上的问题。举例来说,如果一条规则只单独适用于Java微型版(Micro Edition, ME)或Java企业版(Enterprise Edition, EE),而不适用于Java SE版,通常不会包括在本标准内。在Java SE版中,那些处理UI或者在Web接口中提供的特性,比如声音、图形渲染、用户登录控制、会话管理、认证和授权等,不包含在本标准内。然而,这并不妨碍本标准讨论在网络中运行的Java系统由于不正确的用户输入验证而带来的风险、引入的问题和适当的缓解策略。
编码风格。编码风格的问题是主观的,事实已给证明,我们不可能在制定一个适当的样式规则问题上达成共识。因而,本书只推荐了一些用户定义样式的规则,并且一致性地应用这些规则而已,并不强制使用某一种特定的编码规则。如果希望始终如一应用编码风格,最简单的方法是使用代码格式化工具。许多集成开发环境(IDE)提供这样的功能。
工具。美国软件工程研究所(SEI)作为联邦政府资助的研发机构,不会推荐任何特定的厂商和工具,用来强制采纳标准。本书的用户可以自由选择工具,我们鼓励各个厂商为执行这些规则提供相应的工具。
有争议的规则。一般情况下,本书会尽量避免列入那些缺乏广泛共识的有争议规则。
本书读者对象
本书主要是为Java语言开发人员服务的。本标准重点关注Java标准版6.0平台,同时可以为使用Java ME和Java EE或者其他Java语言版本的开发者提供参考。
尽管主要考虑系统的安全性,但是本标准对达到其他质量标准方面也是有参考价值的,比如可靠性、可用性、健壮性、可扩展性、可用性和可维护性等。
本标准还可以用于:
分析工具的开发人员,他们希望能够诊断出那些不安全和不合规则的Java程序。
软件开发经理、软件采购商或者其他希望建立专有的安全编码规范的软件开发及采购人员。
讲授Java安全编码标准的教师,可以使用本标准作为主要的或者参考的软件安全课程教材。
本标准中的一些规则可以扩展为某些组织的特定规则。但这种扩展应该与已有的规则一致,并且和本标准兼容。
关于更好地使用这些安全编码标准,可以开设培训课程。当通过培训课程的考试之后,经过培训的人员可以认证为安全编码方面的专业人士。
内容和组织
本书包括一个介绍性的第1章和其他17章(每一章包含了特定领域的规则)。每一章的规则以一个列表的形式出现,并且包含对这些规则的风险评估摘要。最后是一个术语表和参考资源。可以按顺序阅读,先从本前言开始,然后是概述部分。对于规则部分的章节没有特定的阅读顺序,它们也可以作为参考资料进行查阅。每章的规则都是组织松散的,在一般情况下,可以以任意顺序阅读。
阐述规则时采用了一致的结构。每一个规则都有一个包含在标题中的唯一标识符,在规则的标题和介绍性文字部分阐述符合规则的要求。通常,在后面会有一个或一组不符合规则的代码示例,并且会给出相应的符合规则的方案。同时给出了每个规则的风险评估和具体参考书目。有的规则也列出了相关的漏洞和准则,它们来自于:
《CERT C语言安全编码标准》(The CERT C Secure Coding Standard)[Seacord 2008]
《CERT C++语言安全编码标准》(The CERT C++ Secure Coding Standard)[CERT 2011]
ISO/IEC TR 24772。信息技术-编程语言-通过语言选择和使用以避免在程序语言中出现安全漏洞的指南[ISO/IEC TR 24772:2010]
MITRE CWE [MITRE 2011]
《Java语言安全编码规则第3版》[SCG 2009]
《Java编程风格要素》(The Elements of Java Style)[Rogue 2000]
标识符
每个规则都包含一个唯一标识符,这个标识符包括3个部分:
3个字母的助记符,代表标准的一部分,用来编组类似的规则,方便找到。
两位数值范围为00~99的数字,以确保每个规则都有一个唯一标识符。
字母J表明这是Java语言的规则,之所以包含J,是因为可以防止出现类似CERT的安全编码标准也适用其他语言的情况,从而避免产生歧义。
标识符可以被静态分析工具使用,用于诊断信息的参考规则,也可以直接使用规则标题的简称。
系统质量
在考虑选择和应用编码标准的时候,安全性(security)是一个必须考虑的系统属性。另外,我们感兴趣的属性还包括无害性(safety)、可移植性(portability)、可靠性(reliability)、可用性(availability)、可维护性(maintainability)、可读性(readability)和性能(performance)等。
许多系统属性会以有趣的方式相关联。比如,可读性是可维护性的一个属性;它们对于限制在维护阶段引入缺陷都很重要,而这些缺陷往往会导致安全问题或可靠性问题。另外,可读性会为质量保证人员的代码评审提供帮助。可靠性和可用性需要正确的资源管理,而这种资源管理对提高系统的无害性和安全性有很大的帮助作用。诸如性能和安全性这样的系统特性却是相互冲突的,它们需要有所取舍和相互平衡。
安全编码标准的目的是提高软件的安全性。然而,基于安全和其他系统属性之间的关系,如果其他系统属性也存在对安全性的显著影响,本标准也会包括对它们进行处理的要求和建议。
优先级和层级
每一个规则都分配有优先级。优先级分配使用的度量方式基于FMECA模式(Failure Mode, Effects, and Criticality Analysis, FMECA)[IEC 60812]。为每个规则指定1~3的3个值:
严重性—如果忽略该规则,后果的严重程度:
1 = 低(拒绝访问攻击,非正常终止)
2 = 中(破坏数据一致性,非意愿性的数据泄露)
3 = 高(运行非指定代码,权限升级)
可能性—如果违反该规则,引入缺陷而导致系统出现可被利用漏洞的可能性有多大:
1 = 不可能
2 = 可能
3 = 很有可能
整改代价—如果对现有代码进行整改,使其满足规则所需要付出的代价:
1 = 高(手动发现及修改)
2 = 中(自动发现及手动修改)
3 = 低(自动发现及修改)
对每一条规则,我们把这3个值相乘。该结果提供了一个度量,通过这个度量可以判断应用该规则的优先程度。这个优先程度范围为1~27。优先级在1~4内的规则是3级规则(L3),6~9是第2级(L2),12~27是1级规则(L1)。因此,有可能制定一个标准,实施该标准使得在一个级别的所有规则都需要得到满足,这个级别可以是1级、2级或3级,如图1所示。
图1 层级和优先等级
设计该度量的主要目的是表示为修正项目而付出的努力,并不表示为了实现该标准而需要付出的额外精力。
符合性测试
软件系统可以通过验证,从而符合本书中规定的规则。
规范性文字与非规范性文字
在本书中,属于编码标准的部分被视为规范性的内容,其他部分则作为建议给出。在这些规则中,规范性的描述是符合标准的要求。这些规范性描述使用强制式的语言表述,如“必须”、“应当”、“要求”。尽管对于一些规则而言,实现自动化分析并不具备可能性且并非必要,但是每一个规则的规范性部分必须是可分析的。
非规范性部分描述规则的最佳实践或有益的建议。非规范性描述并不设立符合性要求,它常常使用动词“应该”或者短语如“推荐”、“好的做法”。规则的非规范性部分可能不太适合做自动化检测,主要是因为如果在现有代码中做这种检测,可能存在过多误报。当分析新的代码时(新的代码指按照该编码规范开发的代码),自动化检测也许能够发挥作用。
本书中所有的规则都有一个非规范性部分。这个部分仅应用于以下场合:
遵循广为人知的最佳实践。
如果这条规则描述的方式被广泛采用,对以下两种情况都不产生影响:一是不会违反规则的规范性部分,二是该方法在应用到代码时,是无害的,虽然那些代码并不适用于那些规范性部分。
整个非规范性指导不包含在本编码规则内。然而,本书的作者正在计划发布这些非规范性指导。
自动化分析
为了确保源代码符合本安全编码标准,需要做必要的检查。检查的最有效方法是使用一个或多个分析工具(分析仪)。当一个规则不能由工具检查时,则必须采用人工审核方式。
本书中的许多规则会提示是否已经存在分析工具。这些分析工具可以诊断是否违反规则,甚至可以提示该规则是否可以做自动化检测。这部分的信息可能会有些变化,因为现有的检测工具正在发展中,同时也正开发出新的检测工具。
当选择一个源代码分析工具的时候,理想的状态是,这个工具能够适用于本规范尽可能多的规则。并不是所有的规则都需要自动化检测工具,还是有一些规则需要通过手动检测才能完成的。
完整性和可信性
对于规则来说,一个分析工具在最大程度上应该是完整的,并且是可信的。如果在检测中不会给出假阴性结果,也就说,它能够在一个程序中检测出违反该规则的所有问题,那么称其为可信的。如果不会给出假阳性结果或者误报,则认为该工具的检测是完整的。对于特定的规则,它可能产生的结果如下所示:
表1 可信性和完整性
假阳性
Y N
N 假阳性可信 可信并完整
Y 假阳性不可信 不可信
如果工具有太多的假阳性结果,那么它会浪费开发人员的时间,让他们失去对结果的兴趣,结果是无法得到有用的价值,找到真正的缺陷,因为这些缺陷都淹没在噪声当中了。如果工具有太多假阴性结果,那么会错过许多缺陷,而形成一种虚假的安全感。在实践中,我们需要在这两种情况之间找到平衡点。
在减少假阴性和假阳性上有许多权衡。很显然,最好能够两者都减少,这在一定程度上可以通过许多技巧和算法来帮助实现。
分析应该是一个可信的过程,这意味着我们可以依赖于分析工具的输出结果。因此,开发人员必须保证存在这种信任。在理想情况下,这可以通过工具供应商运行认可的测试来完成。但也存在这种情况,就是若没有正式的验证方案,使用验证套件测试分析工具也是可能的。
CERT源代码分析实验室
CERT创建的源代码分析实验室(Source Code Analysis Laboratory,SCALe),为软件系统提供了适用于CERT安全编码标准的一致性测试,这种测试包括用于Java的CERT Oracle安全编码标准。
SCALe使用多种分析手段评估用户的源代码,包括静态分析工具、动态分析工具、模糊测试等。CERT会向开发人员报告违反安全编码规范的地方。开发人员可以修复这些缺陷,然后再提交软件进行第二次评估。
在开发人员解决SCALe测试出的问题之后,SCALe团队会决定这个版本的软件是否符合CERT标准,并且给开发人员颁发证书,并将该软件系统列入通过该标准的列表。
一个成功的一致性测试表明,SCALe分析并不能检测出所有违反CERT标准中定义的规则。成功的一致性测试也无法保证不会违反这些规则,并且也不能保证这些整个软件永远是安全的。SCALe也不能够测试那些未知的安全漏洞,不能找出概要设计和架构设计的问题,以及代码的运行环境或者可移植性的问题。通过测试的软件仍然可能是不安全的,例如,如果软件实现的是一个不安全的架构和设计。
在本书的规则讨论中,包括了规则的特例以及这些特例产生的情况。当开发人员从一条该规则中引用这些特例时,他们必须在代码中标识出相关的特例信息。至少需要通过注释的方式在注释中标出该特例,如下所示:
// MET12-EX0 applies here
作者目前正在开发一套Java注释,它将允许程序员以可读的和静态分析工具可以访问的方式标识这种特例。通过一致性测试,可以确定异常是否适用于其他情况,这是由SCALe的分析师来完成的。
第三方类库
静态分析工具,如FindBugs这个Java字节码分析工具,经常可以在第三方类库和自定义代码中发现违反安全编码标准的行为。出现在第三方类库中违反安全规则的处理方式是和自定义代码一样的。
遗憾的是,开发人员并不是总能修改第三方类库代码或者说不能说服供应商修改代码。在这种情况下,系统就不能通过一致性测试,除非解决这些问题(将替换该类库,或者开发一个自己的类库)或者将这些问题以文档形式记录下来。对第三方类库中出现的这些问题的处理方式和自定义代码的处理方式是一样的,也就是说,开发人员必须自己能够证明虽违反这些规则中但不会导致安全漏洞。然而它们的代价是各不相同的,对于自定义代码,修改问题可能更经济,而对于第三方类库,文档化问题可能更容易。
一致性测试流程
对于每一个编码标准,源代码分为证实不符、符合和完全符合这几种情况,它们适用于本书中的每一条规则。
证实不符。如果出现一条或多条违例,并且不允许出现偏差,那么称为证实不符。
符合。如果没有发现一条违例,则称为符合。
完全符合。如果验证满足规则的所有情况,那么认为该代码完全符合。
偏差规则
严格遵守所有的规则是不可能的,因此,定义违反特定规则的偏差是必要的。偏差应用在下面这个场合,在这个场合中,一个真阳性的问题作为违例提出,但并不能就此判断代码就是不安全的。这可能是软件的设计和架构特性导致的结果,或者存在这个违例,安全编码规范并没有考虑到这种情况。在这种情况下,定义允许的偏差可以防止标准过度严格。但偏差不能用于解释系统性能、可用性和其他不安全性方面的问题。成功通过一致性测试的一套软件系统,必须不存在编码错误导致的已知漏洞。
偏差要求由主任评估师进行评估,如果开发人员可以提供足够的证据,表示该偏差不会引入安全漏洞,那么可以接纳这个偏差请求。偏差不应该经常使用,因为修复代码缺陷总比证明代码没有缺陷要容易,并且不会导致系统安全漏洞。
一旦已完成评估过程,会有报告详细说明代码是否符合或者不符合安全编码标准的相应规则,并会提供给开发人员。
CERT SCALe认证
被CERT认可符合安全编码规范的开发组织可以在其网站上使用如图2所示的标识。该标识必须指定通过一致性测试的软件,该标识不适用于未经测试的软件、公司和组织。
除了满足以下条件的软件的补丁,对软件进行任何修改之后,都需要进行一致性设计。直至重新测试该软件,并且认定满足一致性要求之后,这个打过补丁的软件才能使用CERT SCALe标识。
满足以下3个标准的软件补丁不违反一致性设计:
该补丁是在代码中修复安全漏洞所必需的,并且是软件维护所必需的。
该补丁不引入新的功能特性。
该补丁不引入违反软件之前通过的那些安全编码规范规则的问题。
使用CERT SCALe标识之后,可以列入软件组织列表,与卡耐基-梅隆大学签订服务协议,软件会被认定为符合CERT标准。更多信息请联系:securecoding@cert.org。
计算机\程序设计
“在Java的世界里,安全性并没有被视为一个附加功能。尽管这是一种普遍的思维方式,但如果不带着安全性去考虑问题,还是会陷入麻烦之中。但并不仅仅因为有了基础设施的帮助,我们就可以想当然地认为安全性已经被自动考虑了。近些年来,业界发展出了一系列的安全标准和最佳实践。这本书就是一本这些实践的经验总结。它们并不是理论研究论文或者说市场营销的噱头。它们都是在重要项目中经过严格监测的可以在企业级规模应用的产物。”
——James A. Gosling Java语言之父
使用Java语言安全编码的一个关键组成部分是具有一部良好文档化和可直接使用的编码标准。编码标准鼓励开发人员遵从一个统一的规则集合,而这个规则集合往往是根据项目或者组织的需求来确定的,而不是根据开发人员自身的熟悉和喜好来制定的。一旦建立了规范,这些标准可以用于评估源代码的度量(通过手动或者自动的过程)。
本书提供了一系列的规则,这些规则可以用于消除那些可能会造成软件出现被利用的安全漏洞的不安全编码实践。通过运用标准规则可以让开发人员设计出更高质量的系统,从而提供可以对抗更多攻击的健壮系统。这些规则可以应用于使用Java进行开发的各种广泛的产品,如PC、游戏机、移动电话、家用电器以及自动化电子设备等。
本书首先从总体上概述了Java应用安全,然后通过17个组织形式一致的章节来对各个关键领域的特定的详细规则进行了阐述。在每一个领域中,作者展示了不符合规则的代码示例和相对应的解决方案,展示了如何评估风险,并且提供了更多有用信息的索引。每一章中的每一条规则都是根据产生结果的严重程度、引入安全漏洞的可能性,以及弥补代价的大小来进行排序的。
本书提供了对于Java SE 6平台的安全编码规范,它包括Java编程语言和相关的类库,并且对Java SE 7平台中的新特性进行了讨论。本书通过描述受JVM和编译器实现所影响的语言行为,指导开发人员能够正确使用Java API和安全架构,以及在使用标准API扩展(从javax包层次中)的时候考虑安全性的问题。这个标准覆盖了应用于以下类库的安全问题:lang、util、Collections、Concurrency Utilities、Logging、Management、Reflection、Regular Expressions、 Zip、 I/O、JMX、JNI、Math、Serialization、JAXP。
CERT项目是美国卡耐基-梅隆大学软件工程研究所的一个项目,而软件工程研究所本身是由美国联邦资助的研究和开发机构。CERT致力于研究互联网安全漏洞、网络系统中的长期演变,并且开发相关的咨询和培训项目来帮助各种组织改进安全问题。Oracle作为一个提供商,为客户提供完整的、开放的和集成的商业软件和硬件系统。它现在同时也掌握着Java的发展。在CERT和Oracle的支持下,同时也在其他的Java安全编码专家如本书各位作者的支持下,使得这些标准得以开发、测试并且通过文档记录下来,从而汇集成本书。
(美)Fred Long Dhruv Mohindra Robert C. Seacord Dean F. Sutherland David Svoboda 著:Fred Long 英国Aberystwyth大学计算机科学系高级讲师和教学主任。主要讲授形式方法、Java、C++和C的编程模式以及与编程相关的安全问题的课程。还是英国计算机协会的中威尔士分会的主席。自1992年以来在软件工程研究所担任客座研究员。最近正在研究如何在Java中探查安全性漏洞。 Dhruv Mohindra 印度Persistent 系统工程有限公司的高级软件工程师。曾研发了广泛应用于企业服务器的监控软件。曾在软件工程研究所的CERT项目工作,并致力于在编程社区中提高对安全问题的警觉性。曾任职于卡耐基-梅隆大学,拥有信息安全策略与管理硕士学位和印度Pune大学的计算机工程学士学位。他在开发和增强SOA系统的安全性、服务器监控软件、移动设备应用、基于Web的数据挖掘以及如何设计用户友好的安全界面拥有自己的深刻理解。 Robert C. Seacord 资深计算机安全专家和作家。在计算机安全、历史系统改造以及基于组件的软件工程等领域具有极深的造诣。目前管理卡耐基-梅隆大学软件工程研究所CERT在安全编码领域的创新项目。曾在IBM公司从事软件编程工作,开发过通信和操作系统软件、处理器软件以及软件工程方面的工作。拥有Rensselaer Polytechnic学院的计算机科学学士学位。 Dean F. Sutherland CERT高级软件安全工程师,编译器后端技术专家组的高级专家。拥有卡耐基-梅隆大学博士学位。曾担任职业软件工程师,在Tartan公司工作了14年。 David Svoboda CERT软件安全工程师,资深Java开发工程师,在Java开发领域拥有13年的开发经验。。是卡耐基-梅隆大学的一系列软件开发项目的主要开发者,这些项目涉及从层级芯片建模到社会组织仿真到自动机器学习等多个方面。同时积极参与了若干个ISO标准组的活动,包括C编程语言的JTC1/SC22/WG14标准组和C++语言的JTC1/SC22/WG21标准组等。
计文柯 杨晓春 译:暂无简介
随着软件技术的发展,特别是互联网的发展,在很多应用成为互联网应用之后,软件系统本身变成了一个开放的系统,其中也包括潜在用户数据对系统外的开放,不管是开发人员还是软件用户,除了关注软件功能实现本身,对软件安全性的关注也越来越多。
在软件开发中,由于Java语言具有开放性和可移植性,它逐渐成为构建应用的主要开发环境和语言之一,而对安全性的关注也开始成为一个热点话题。正如在本书序中,Java语言之父James Golsing说的:“在Java的世界里,安全性并没有被视为一个附加功能。尽管这是一种普遍的思维方式,但如果不带着安全性去考虑问题,还是会陷入麻烦之中。”从Golsing的观点中,我们可以看出,对代码安全性的设计和考虑是需要系统学习的,因为这是一种对思维方式的反省。本书就是为了帮助开发人员加强对Java语言安全性的认识而推出的技术专著,书中结合Java应用的开发和Java语言的使用,系统地阐述了使用Java语言时的相关安全规则。本书内容非常全面,包括基于Java标准版6.0平台(Java SE 6)环境中一系列应用于Java编程语言和类库的安全编码规则,并且对这一系列规则进行了分类,包括输入数据验证、声明和初始化、表达式、数值类型和操作、面向对象、方法使用、异常处理、可见性和原子性、锁、线程、输入输出、序列化、平台安全特性、Java运行环境等各个基本的分类部分。读者可以将本书作为Java安全方面的工具书,根据自己的需要,找到感兴趣的规则进行阅读和学习,或者在开发工作和软件设计中,当遇到安全问题时,根据书中列出的大致分类对规则进行检索和阅读,另外,也可以通读全书的所有规则,系统地了解Java安全规则,增强对Java安全特性、语言使用、运行环境特性的理解。在国内的技术书籍中,像本书这样系统阐述Java安全的书籍并不多见,因此本书值得对Java安全感兴趣的读者系统阅读。
译者力争以通俗通畅的中文再现原著的经典知识,希望尽自己的努力,帮助读者学习这部经典著作。但由于译者水平有限,不管是在Java安全的专业领域知识,还是在对原文的理解上,甚至在中文遣词造句的表达上,在翻译的作品中,肯定会在诸多方面存在疏漏之处,还请读者不吝赐教。您的意见、建议将帮助我们改善本书的质量。欢迎将关于本书的任何问题和看法发邮件到jiwenke@gmail.com,与我们交流本书相关的信息。再次感谢!
译者序
序
前言
致谢
第1章 概述1
1.1 错位的信任1
1.2 注入攻击2
1.3 敏感数据泄露3
1.4 效能泄露5
1.5 拒绝服务6
1.6 序列化8
1.7 并发性、可见性和内存8
1.8 最低权限原则14
1.9 安全管理器15
1.10 类装载器16
1.11 小结16
第2章 输入验证和数据净化(IDS)17
规则17
风险评估概要17
2.1 IDS00-J净化穿越受信边界的非受信数据18
2.2 IDS01-J验证前标准化字符串26
2.3 IDS02-J在验证之前标准化路径名28
2.4 IDS03-J不要记录未经净化的用户输入31
2.5 IDS04-J限制传递给ZipInputStream的文件大小33
2.6 IDS05-J使用ASCII字符集的子集作为文件名和路径名35
2.7 IDS06-J从格式字符串中排除用户输入37
2.8 IDS07-J不要向Runtime.exec() 方法传递非受信、未净化的数据38
2.9 IDS08-J净化传递给正则表达式的非受信数据41
2.10 DS09-J如果没有指定适当的locale,不要使用locale相关方法处理与locale相关的数据44
2.11 IDS10-J不要拆分两种数据结构中的字符串45
2.12 IDS11-J在验证前去掉非字符码点50
2.13 IDS12-J在不同的字符编码中无损转换字符串数据51
2.14 IDS13-J在文件或者网络I/O两端使用兼容的编码方式53
第3章 声明和初始化(DCL)56
规则56
风险评估概要56
3.1 DCL00-J防止类的循环初始化56
3.2 DCL01-J不要重用Java标准库的已经公开的标识59
3.3 DCL02-J将所有增强for语句的循环变量声明为final类型60
第4章 表达式(EXP)63
规则63
风险评估概要63
4.1 EXP00-J不要忽略方法的返回值63
4.2 EXP01-J不要解引用空指针65
4.3 EXP02-J使用两个参数的Arrays.equals()方法来比较两个数组的内容67
4.4 EXP03-J不要用相等操作符来比较两个基础数据类型的值67
4.5 EXP04-J确保使用正确的类型来自动封装数值72
4.6 EXP05-J不要在一个表达式中对同一变量进行多次写入73
4.7 EXP06-J不要在断言中使用有副作用的表达式76
第5章 数值类型与运算(NUM)78
规则78
风险评估概要78
5.1 NUM00-J检测和避免整数溢出79
5.2 NUM01-J不要对同一数据进行位运算和数学运算85
5.3 NUM02-J确保除法运算和模运算中的除数不为088
5.4 NUM03-J使用可容纳无符号数据合法取值范围的整数类型89
5.5 NUM04-J不要使用浮点数进行精细计算90
5.6 NUM05-J不要使用非标准化数92
5.7 NUM06-J使用strictfp修饰符确保跨平台浮点运算的一致性94
5.8 NUM07-J不要尝试与NaN进行比较97
5.9 NUM08-J检查浮点输入特殊的数值98
5.10 NUM09-J不要使用浮点变量作为循环计数器100
5.11 NUM10-J不要从浮点字元构造BigDecimal对象101
5.12 NUM11-J不要比较或者审查以字符串表达的浮点数值102
5.13 NUM12-J确保将数值转换成较小类型时不会产生数据丢失或曲解103
5.14 NUM13-J转换基本整数类型至浮点类型时应避免精度损失107
第6章 面向对象(OBJ)110
规则110
风险评估概要110
6.1 OBJ00-J只有受信子类能对具有不变性的类和方法进行扩展111
6.2 OBJ01-J声明数据成员为私有并提供可访问的封装器方法116
6.3 OBJ02-J当改变基类时,保存子类之间的依赖关系118
6.4 OBJ03-J在新代码中,不要混用具有泛型和非泛型的原始数据类型124
6.5 OBJ04-J为可变类提供复制功能,并通过此功能允许将实例传递给非受信代码128
6.6 OBJ05-J在返回引用之前,防御性复制私有的可变的类成员132
6.7 OBJ06-J对可变输入和可变的内部组件创建防御性复制136
6.8 OBJ07-J不允许敏感类复制其自身138
6.9 OBJ08-J不要在嵌套类中暴露外部类的私有字段141
6.10 OBJ09-J比较类而不是类名称143
6.11 OBJ10-J不要使用公有静态的非final变量144
6.12 OBJ11-J小心处理构造函数抛出异常的情况146
第7章 方法(MET)153
规则153
风险评估概要153
7.1 MET00-J验证方法参数154
7.2 MET01-J不要使用断言验证方法参数156
7.3 MET02-J不要使用弃用的或过时的类和方法157
7.4 MET03-J进行安全检测的方法必须声明为private或final158
7.5 MET04-J不要增加被覆写方法和被隐藏方法的可访问性160
7.6 MET05-J确保构造函数不会调用可覆写的方法161
7.7 MET06-J不要在clone()中调用可覆写的方法163
7.8 MET07-J不要定义类方法来隐藏基类或基类接口中声明的方法165
7.9 MET08-J确保比较等同的对象能得到相等的结果167
7.10 MET09-J定义了equlas()方法的类必须定义hashCode()方法174
7.11 MET10-J实现compareTo()方法时遵守常规合约176
7.12 MET11-J确保比较中的关键码是不可变的178
7.13 MET12-J不要使用析构函数182
第8章 异常行为(ERR)187
规则187
风险评估概要187
8.1 ERR00-J不要消除或忽略可检查的异常187
8.2 ERR01-J不能允许异常泄露敏感信息192
8.3 ERR02-J记录日志时应避免异常196
8.4 ERR03-J在方法失败时恢复对象先前的状态197
8.5 ERR04-J不要在finally程序段非正常退出201
8.6 ERR05-J不要在finally程序段中遗漏可检查异常202
8.7 ERR06-J不要抛出未声明的可检查异常205
8.8 ERR07-J不要抛出RuntimeException、Exception或Throwable209
8.9 ERR08-J不要捕捉NullPointerException或任何它的基类210
8.10 ERR09-J禁止非受信代码终止JVM216
第9章 可见性和原子性(VNA)219
规则219
风险评估概要219
9.1 VNA00-J当需要读取共享基础数据类型变量时,需要保证其可见性219
9.2 VNA01-J保证对一个不可变对象的共享引用的可见性222
9.3 VNA02-J保证对于共享变量的组合操作是原子性的225
9.4 VNA03-J即使每一个方法都是相互独立并且是原子性的,也不要假设一组调用是原子性的230
9.5 VNA04-J保证串联在一起的方法调用是原子性的235
9.6 VNA05-J保证在读写64位的数值时的原子性239
第10章 锁(LCK)241
规则241
风险评估概要241
10.1 LCK00-J通过私有final锁对象可以同步那些与非受信代码交互的类242
10.2 LCK01-J不要基于那些可能被重用的对象进行同步246
10.3 LCK02-J不要基于那些通过getClass()返回的类对象来实现同步249
10.4 LCK03-J不要基于高层并发对象的内置锁来实现同步252
10.5 LCK04-J即使集合是可访问的,也不要基于集合视图使用同步253
10.6 LCK05-J对那些可以被非受信代码修改的静态字段,需要同步进入255
10.7 LCK06-J不要使用一个实例锁来保护共享静态数据256
10.8 LCK07-J使用相同的方式请求和释放锁来避免死锁258
10.9 LCK08-J在异常条件时,保证释放已经持有的锁266
10.10 LCK09-J不要执行那些持有锁时会阻塞的操作270
10.11 LCK10-J不要使用不正确形式的双重锁定检查惯用法273
10.12 LCK11-J当使用那些不能对锁策略进行承诺的类时,避免使用客户端锁定277
第11章 线程API(THI)282
规则282
风险评估概要282
11.1 THI00-J不要调用Thread.run()282
11.2 THI01-J不能调用ThreadGroup方法284
11.3 THI02-J通知所有等待中的线程而不是单一线程287
11.4 THI03-J始终在循环中调用wait()和await()方法292
11.5 THI04-J确保可以终止受阻线程295
11.6 THI05-J不要使用Thread.stop()来终止线程300
第12章 线程池(TPS)304
规则304
风险评估概要304
12.1 TPS00-J使用线程池处理流量突发以实现降低性能运行304
12.2 TPS01-J不要使用有限的线程池来执行相互依赖的任务307
12.3 TPS02-J确保提交至线程池的任务是可中断的312
12.4 TPS03-J确保线程池中正在执行的任务不会失败而不给出任何提示315
12.5 TPS04-J使用线程池时,确保ThreadLocal变量可以重新初始化318
第13章 与线程安全相关的其他规则(TSM)323
规则323
风险评估概要323
13.1 TSM00-J不要使用非线程安全方法来覆写线程安全方法323
13.2 TSM01-J不要让this引用在创建对象时泄漏326
13.3 TSM02-J不要在初始化类时使用后台线程332
13.4 TSM03-J不要发布部分初始化的对象336
第14章 输入输出(FIO)342
规则342
风险评估概要342
14.1 FIO00-J不要操作共享目录中的文件343
14.2 FIO01-J使用合适的访问权限创建文件351
14.3 FIO02-J发现并处理与文件相关的错误352
14.4 FIO03-J在终止前移除临时文件354
14.5 FIO04-J在不需要时关闭资源357
14.6 FIO05-J不要使用wrap()或duplicate()创建缓存,并将这些缓存暴露给非受信代码361
14.7 FIO06-J不能在一个单独的InputStream上创建多个缓存区封装器364
14.8 FIO07-J不要让外部进程阻塞输入和输出流367
14.9 FIO08-J对读取一个字符或者字节的方法,使用int类型的返回值370
14.10 FIO09-J不要使用write()方法输出超过0~255的整数372
14.11 FIO10-J使用read()方法保证填充一个数组373
14.12 FIO11-J不要将原始的二进制数据作为字符数据读入375
14.13 FIO12-J为小端数据的读写提供方法376
14.14 FIO13-J不要在受信边界之外记录敏感信息379
14.15 FIO14-J在程序终止时执行正确的清理动作381
第15章 序列化(SER)387
规则387
风险评估概要387
15.1 SER00-J在类的演化过程中维护其序列化的兼容性388
15.2 SER01-J不要偏离序列化方法的正确签名390
15.3 SER02-J在将对象向信任边界之外发送时,需要签名并且封装敏感对象392
15.4 SER03-J不要序列化未经加密的敏感数据397
15.5 SER04-J不要允许序列化和反序列化绕过安全管理器401
15.6 SER05-J不要序列化内部类实例404
15.7 SER06-J在反序列化时,对私有的可变的组件进行防御性复制405
15.8 SER07-J不要对实现定义的不可变因素使用默认的序列化格式406
15.9 SER08-J在从拥有特性的环境中进行反序列化之前最小化特权410
15.10 SER09-J不要从readObject()方法中调用可以被覆写的方法413
15.11 SER10-J在序列化时,避免出现内存和资源泄漏414
15.12 SER11-J防止覆盖外部化的对象415
第16章 平台安全性(SEC)417
规则417
风险评估概要417
16.1 SEC00-J不要允许特权代码块越过受信边界泄露敏感信息417
16.2 SEC01-J不要在特权代码块中使用污染过的变量420
16.3 SEC02-J不要基于非受信源进行安全检查422
16.4 SEC03-J不要在允许非受信代码装载任意类之后装载受信类424
16.5 SEC04-J使用安全管理器检查来保护敏感操作426
16.6 SEC05-J不要使用反射来增加类、方法和字段的可访问性429
16.7 SEC06-J不要依赖于默认的由URLClassLoader和java.util.jar提供的自动化签名检查434
16.8 SEC07-J当编写一个自定义的类装载器时调用基类的getPermissions()方法437
16.9 SEC08-J定义基于原生方法的封装器438
第17章 运行环境(ENV)441
规则441
风险评估概要441
17.1 ENV00-J不要签名只执行非特权操作的代码441
17.2 ENV01-J将所有安全敏感的代码置于单独一个jar包中,并且在签名之后封装它443
17.3 ENV02-J不要信任环境变量的值446
17.4 ENV03-J不要赋予危险的权限组合448
17.5 ENV04-J不要关闭字节码验证功能451
17.6 ENV05-J不要部署一个被远程监视的应用452
第18章 其他(MSC)457
规则457
风险评估概要457
18.1 MSC00-J在交换安全数据时使用SSLSocket而不是Socket457
18.2 MSC01-J不要使用空的无限循环461
18.3 MSC02-J生成强随机数462
18.4 MSC03-J不要硬编码敏感信息464
18.5 MSC04-J防止内存泄漏466
18.6 MSC05-J不要耗尽堆空间473
18.7 MSC06-J当一个遍历正在进行时,不要修改它对应的集合477
18.8 MSC07-J防止多次实例化单例对象481
术语表490
参考资源497