1、第3章 软件安全开发教学目标 应掌握的知识要点:安全的软件开发周期;安全原则、规则及规章;安全需求:攻击用例;架构和设计评审/威胁建模;安全的编码原则;白盒/黑盒/灰盒测试;判定可利用性;软件的安全部署;软件安全编程。重点 安全的软件开发生命周期 软件安全编程 Web编程安全本节安排安全的软件开发周期软件开发语言内存安全进程和线程安全异常与错误处理输入安全3.1 安安全的软件开发周期全的软件开发周期定义 SSDL描绘了一种结构化方法,用以贯彻和实现安全的软件开发。SSDL方法体现了现代化快速应用程序开发研究计划带来的好处。这类研究计划在早期就开始考虑风险,贯穿每次软件构建的分析、设计和开发过程
2、,这已经日益成为一种流行的做法。3.1 安全的软件开发周期SSDL遵循的评估准则完备性 评估安全需求详尽定义的完备程度一致性 确保每项需求都不会与其他需求相矛盾可行性 评估可行性程度,即评估需求能切实使用现有技术实现的程度可测试性可测试性 评估可测试性,即评估测试方法能在多大程度上证明安全需求已经成功实现优先级优先级 帮助每个人理解就风险承担而言的需求的相对值规章规章 要求这种安全需求与本项目必须遵守的规章所规定的安全需求相符合3.1.1 安全的软件开发周期概述SSDL的组成部分3.1.1 安全的软件开发周期概述安全原则、规则及规章安全需求架构、设计评审和威胁建模安全的编码原则白盒、黑盒、灰盒
3、测试判定可利用性作用基于特定的官方规章来定义一份系统范围的规范,在其中定义将应用到本系统的安全需求。3.1.2 安全原则、规则及规章常见安全规范Sarbanes-Oxley Act of 2002(SOX)(The Graham-Leech-BlileyPayment Card Industry Data Security Standard3.1.2 安全原则、规则及规章安全需求获取过程3.1.3 安全需求获取安全需求的方法基于传统用例传统的用例设计方法只能用来满足功能性需求分析,对于安全需求的特殊性无法起到有效的作用基于问题框架它是从攻击者角度考虑系统安全问题反模型针对面向目标的需求分析方法
4、不能捕获由恶意的攻击行为所产生的障碍目标的缺点而给出的障碍目标获取方法和消解恶意障碍目标的方法3.1.3 安全需求反模型获取安全需求的步骤从系统中找出一些安全安全关键性的对象并为它们定义安全关注点;分析初始的反目标,识别出具有反目标意图的攻击者;进一步分析攻击者的动机,尽量获得攻击者的真实意图,得到更多的反目标;精简步骤3)中获取的反目标,直到反目标可以被攻击者直接实施或者可具体化为软件的防御漏洞。根据反目标,建立攻击者与软件系统之间的“发模型”;消解反目标,主要采用反目标弱化、反目标消除等手段,这些手段可作为安全目标。3.1.3 安全需求安全需求实例该应用程序将存储敏感的用户信息,这些信息必
5、须得到与HIPAA兼容标准的保护。为达到该目标,无论在哪里存储,都必须使用强加密来保护所有敏感的用户数据。该应用程序支持具有不同权限等级的多用户使用,为用户分配了多种权限等级,并定义了每一权限等级所授权执行的操作。需要定义并测试各种不同的权限等级和绕过授权攻击的缓解措施。该应用程序接受用户的输入,并将使用SQL。需要定义SQL注入攻击的缓解措施。必须对用户输入进行长度和字符有效性验证(必须定义合法的字符数据元素)。该系统需要对各个用户及其身份鉴别保持跟踪。要求密码有关的涉密信息必须安全地存储。该应用程序使用C或C+编写。代码必须以这样的方式编写:始终跟踪并检查缓冲区长度;用户输入不能更改格式化
6、字符串;整数值不允许溢出。若编译器支持栈探测方法,那就用这种方法。3.1.3 安全需求安全团队及早介入的好处安全团队及早介入可防止形成不安全的体系结构和安全性欠缺的设计,同样也有助于消除项目生命周期后期可能出现的应用程序行为的混乱。此外,及早介入还可使安全专家能够了解应用程度的哪些方面最关键,而从安全角度来看哪些要素风险最高。3.1.4 架构、设计评审和威胁建模威胁建模威胁建模-就是用来排定安全测试优先级的一种技术。通过威胁建模,在对应用程序的设计加以理解的基础上,可假定潜在的安全风险并对其进行评价。然后,根据攻击难易程度和攻击的影响严重性,将这些威胁进行分级并依次消除。3.1.4 架构、设计
7、评审和威胁建模建模过程威胁建模的好处威胁建模的好处在于能发现的问题与代码评审及测试的不同,能发现较高层次的设计问题,而不是实现方面的错误。通过威胁建模能够在编码实现为产品之前,及早发现安全问题。这有助于判断出应用程序的“风险最高”部分,这些都需要在整个安全开发工作中进行非常详细的审查。威胁建模另一个非常有价值的方面就是能让人有一种完备的感觉。3.1.4 架构、设计评审和威胁建模设计漏洞设计漏洞是一种设计中的缺陷,它使得程序不能安全地运行,而无论代码编写者如何完美地实现了这个程序都无济于事。实现漏洞是在实际软件代码编写中导致的安全性错误。3.1.5 安全编码原则静态分析工具及第三方检测静态分析工
8、具可通过扫描源代码或二进制静态分析工具可通过扫描源代码或二进制的可执行文件检测出许多实现错误。这些的可执行文件检测出许多实现错误。这些工具对于找出类似缓冲区溢出这样的问题工具对于找出类似缓冲区溢出这样的问题是非常有用的,其输出能帮助开发人员学是非常有用的,其输出能帮助开发人员学习在开始的时候就对这些错误进行预防。习在开始的时候就对这些错误进行预防。还可以使用第三方提供的服务,将代码发还可以使用第三方提供的服务,将代码发送给他们,由他们来分析并找出其中的缺送给他们,由他们来分析并找出其中的缺点。使用第三方服务的好处是他们会从适点。使用第三方服务的好处是他们会从适应性因素或用户需求方面来验证代码的
9、安应性因素或用户需求方面来验证代码的安全性。全性。3.1.5 安全编码原则白盒测试白盒测试常用于质量保证领域。可以认为是一种内部攻击。测试人员能够访问源代码和设计文档,这使得测试人员能够高效地工作。他们可以进行威胁建模或逐行审查代码,查找信息来指导测试数据的选择。白盒测试是找出安全漏洞最有效的方法,更多的信息可使测试接口更快且完整地生成。3.1.6 白盒、灰盒与黑盒测试黑盒测试黑盒测试是指以局外人的身份对系统进行分析,使用工具来检测系统的攻击面,并探查系统的内部信息。在没有系统内部知识时,测试人员要获得系统的情况。这时,信息泄露对于黑盒测试人员来说尤为重要,这是因为,相对于操作那些没有信息泄露
10、的程序来说,这些泄露出来的信息有助于测试人员获得系统更多的情况。3.1.6 白盒、灰盒与黑盒测试灰盒测试将软件在调试器中运行而加以测试是一种理想方式,通过这种方式,可混合使用黑盒测试和源代码,从而使测试人员获得灰盒测试带来的好处。3.1.6 白盒、灰盒与黑盒测试灰盒测试的好处如果在代码开发期间发现了问题,就要立即修补潜在可利用性的代码。但是,出于对打补丁开销的考虑,许多开发团队并不喜欢修补那些已经发布的或者已经投入生产的代码的安全缺陷。用于发现漏洞的灰盒方法为开发团队提供了关键的可利用性信息,这可以帮助他们就是否修补代码中的潜在漏洞做出决定。3.1.6 白盒、灰盒与黑盒测试判定漏洞的可利用性的
11、权衡因素攻击者企图探测并利用这个漏洞所需的访问权限和定位技术成功利用此漏洞能获得的访问级别或权限探测并利用此漏洞的时间和工作量因素探测和利用潜在的可靠性探测和利用尝试行为的可重复性3.1.7 判定可利用性判定漏洞的可利用性的权衡因素(续)时间某些漏洞可能需要相当长的时间来探测并利用。可靠性和再现性漏洞的严重程度取决于漏洞可被攻击者利用的可靠性和再现性如何。可靠性和再现性与漏洞是低级别的还是高级别的有关。访问利用漏洞通常能为攻击者提供比之前更多的访问权。通过漏洞得到的这种访问授权,其形式可能是用户权限、网络访问或对其他保护资源的访问权。定位要利用一个漏洞,攻击者必须能与这个存在漏洞的应用程序进行
12、交互,并能访问到含有该漏洞的代码。是否可以通过Internet进行攻击由这个应用程序是不是网络软件以及使用哪种协议进行通信决定。3.1.7 判定可利用性定义计算机软件的部署指计算机软件投入使用过程中的所有相关环节,部署软件系统涉及从计算机软件源头(可以理解为服务器端)将软件组成部分传递或复制到客户端,一旦软件被部署,客户就能使用软件系统。3.1.8 软件的安全部署意义软件部署的结果对软件系统运行时的性能,如反馈时长、系统稳定性、输入输出的数据量、易用性、安全指标等,有很重要的影响,而且这种影响作用对分布式软件系统更加明显。近年来,如何实现软件系统的优化部署是软件部署和分布式系统研究的重要内容。
13、随着软件系统复杂性和软件运行环境复杂性的不断增加,软件部署在软件生命周期中变得越来越重要。3.1.8 软件的安全部署软件部署目前存在的问题软件规模庞大工作环境多样性自调整问题适应问题安全问题3.1.8 软件的安全部署角色和职责问题在安全的软件开发生命周期中,安全是许多人的职责。不能将安全仅依赖于网络小组安装入侵检测系统和防火墙,以及运行一些用于安全的网络工具。重要的是要对角色和职责进行定义,这样,每个人都知道由谁测试什么内容,举例来讲,这样就可以使得应用程序测试团队不再假定网络测试工具也会捕捉到应用程序的安全漏洞。项目经理或产品经理应该编写安全策略基于指定的相关标准来编写。架构师和开发人员负责
14、提供设计和实现细节、确定和研究安全威胁以及执行代码评审工作。测试人员驱动系统的关键分析、参加安全建模工作、确定和研究安全威胁并构建白盒和黑盒测试。项目经理管理计划进度,并控制每份文档及其日期。安全过程经理可检查指导安全建模、安全评估以及安全编码培训。.1.9 软件安全开发中的角色和职责3.2 软软件安全编程件安全编程C/C+1972年,贝尔实验室Dennis Ritchie开发了C语言。从此C语言成为专业程序员使用的主流语言,也成为UNIX操作系统的主流语言。后来在C语言中融入了面向对象的特性,如封装和继承,最初成为C classes,1983年该语言被改称为C+。由于C/C+是在Intern
15、et之前发展起来的,安全性是后来补充进去的。3.2.1 软件开发语言JAVAJAVA是SUN公司开发的面向对象程序设计语言,它具有平台无关性和自动收集垃圾等功能,并且保留了C/C+类似的语法。JAVA相对于C/C+,它解决了后者存在的漏洞,提供了成熟的内存管理和数组边界检测机制。“沙盒”技术是发现可疑行为后让程序继续运行,当发现的确是病毒时才会终止。3.2.1 软件开发语言C#C#是微软公司发布的语言。它也是一种平台无关、面向对象的开发语言。C#的安全性是作为.N ET运行时的一部分来实现的。因此C#提供了一下几个内部安全特性:许可权:命名空间负责处理所有的代码许可功能。安全机制:管理员可以创
16、建安全机制来限制代码能执行的操作。Principal:一个principal完成用户的一个动作。Principal是使用principal代理提供的凭证来完成认证的。.NET确保了代码只能完成授权执行的操作。类型安全:C#提供了可选的类型安全,以保证代码只能访问已授权的内存位置。3.2.1 软件开发语言PerlPerl是由流行的基于UNIX的脚本语言引擎的特性和功能组合成的核心语言。3.2.1 软件开发语言内存数据任何开发语言都不能避开对内存的使用,所以内存安全关系到整个程序的安全,在软件开发和程序设计中占有重要地位。内存数据涵盖了很多方面,如字符串、数组、整数都在内存中以不同形式存在,它们在
17、被操作的过程中,具有哪些特点,什么样的操作能够产生攻击,这些都是程序员编程时需要重视的问题。3.2.2 内存安全缓冲区溢出缓冲区溢出指的是一种常见且危害很大的系统攻击手段,通过向程序的缓冲区写入超出其长度的内容,造成缓冲区的溢出,从而破坏程序的堆栈,使程序转而执行其他的指令,以达到攻击的目的。3.2.2 内存安全缓冲区溢出漏洞攻击步骤在程序的地址空间放入一些攻击性数据,故意让缓冲区溢出。直接输入法、传递参数法精心设计溢出的数据,让程序执行攻击者预想的功能,也就是改变程序的执行流程,跳转到攻击者安排的攻击代码。利用另一个函数的返回地址、直接利用函数指针3.2.2 内存安全缓冲区溢出漏洞防范积极检
18、查边界不让攻击者执行缓冲区内的命令编写风格良好的代码程序指针检查3.2.2 内存安全概念进程是程序在计算机上的一次执行活动,是操作系统进行资源分配的单位,通俗地讲,是正在执行的程序。线程是进程中的一个实体,是被系统独立调度和分派的基本单位,它可与同属一个进程的他线程共享进程所拥有的全部资源。3.2.3 进程和线程安全线程的生命周期创建状态:使用new运算符创建一个线程后,该线程仅是一个空对象,系统没有分配资源;可运行状态:使用start()方法启动一个线程后,系统分配了资源,使该线程处于可运行状态(Runnable);运行中状态:占有CPU,执行线程的run()方法;阻塞状态:运行的线程因某种
19、原因停止继续运行;死亡状态:线程结束。3.2.3 进程和线程安全线程的安全性起因多个线程之间可能会共享进程的内存资源。CPU的某个时间片分配给哪个线程使用,默认情况下无法由用户控制。3.2.3 进程和线程安全同步在发出一个功能调用时,在得到结果之前该调用就不返回,同时其他线程也不能调用这个方法。通俗地讲,一个线程能否抢占CPU,必须考虑另一个线程中的某种条件,而不能随便让操作系统按照默认方式分配CPU,如果条件不具备,就应该等待另一个线程运行,直到条件具备。3.2.3 进程和线程安全死锁指两个或两个以上的线程在执行过程中,因争夺资源而造成的一种互相等待的现象。此时称系统处于死锁状态,这些永远在
20、互相等待的线程称为死锁线程。3.2.3 进程和线程安全产生死锁的必要条件是互斥条件,资源每次互斥条件,资源每次只能被一个线程使用;只能被一个线程使用;请求与保持条件,一请求与保持条件,一个进程因请求资源而个进程因请求资源而阻塞时,对已获得的阻塞时,对已获得的资源保持不放;资源保持不放;不剥夺条件,进程已不剥夺条件,进程已获得的资源,在未使获得的资源,在未使用完之前,不管其是用完之前,不管其是否阻塞,无法强行剥否阻塞,无法强行剥夺;夺;循环等待条件,若干循环等待条件,若干进程之间互相等待,进程之间互相等待,形成一种头尾相接的形成一种头尾相接的循环等待资源关系。循环等待资源关系。3.2.3 进程和
21、线程安全线程控制对线程生命周期的操作stop(),停,停止线程;止线程;suspend(),暂停线程的暂停线程的运行;运行;resume(),继续线程的继续线程的运行;运行;destroy(),让线程销毁。让线程销毁。3.2.3 进程和线程安全线程生命周期中的安全问题线程暂停或终止时,可能对某些资源的锁并没有释放,它所保持的任何资源都会保持锁定状态;线程暂停后,我们无法预计它什么时候会继续(一般与用户操作有关),如果对某个资源的锁长期被保持,其他线程在任何时候都无法再次访问该资源,极可能造成死锁。针对这个问题,为减少出现死锁的可能,Java 1.2中将Thread类的stop()、suspen
22、d()、resume()以及destroy()方法定义为“已过时”方法,不再推荐使用。3.2.3 进程和线程安全进程的组成及状态进程控制块程序段数据段运行阻塞就绪3.2.3 进程和线程安全进程的安全从开发者(编程)角度看,进程的安全所需要考虑的问题与线程类似,但由于线程能够共享进程的资源,所以线程安全一般考虑的问题比进程安全要多。不过,对于开发多个进程能够运行的系统软件(如操作系统),进程的安全就应该重点考虑了。3.2.3 进程和线程安全开发过程中的常见问题编译错误编译错误运行错误运行错误逻辑错误逻辑错误3.2.4 异常与错误处理中的安全常见异常访问数据库时,数据库停止工作;访问文件,文件恰好
23、被另一个程序访问;输入一个以0作为除数的数值;类型转换、对象未分配内存等。3.2.4 异常与错误处理中的安全异常处理当系统底层出现异常时,实际上是将异常用对象封装起来,传给调用方(客户端),俗称抛出(throw)。如在程序里面发生了数字格式异常,这个异常在底层就被封装成java.lang.NumberFormatException对象抛出。异常对象抛出给函数的调用者,如果调用者具有对异常处理的代码,则对异常进行处理;否则将异常继续向前抛出;如果直到用户端还没有对异常进行处理,异常将在标准输出(如控制台)中打印。对于非面向对象语言,异常出现的原理类似。3.2.4 异常与错误处理中的安全捕捉异常用
24、try块将可能出现异常的代码包起来;用catch块来捕获异常并处理异常;如果有一些工作是不管异常是否出现都要执行的,则将相应的代码用finally块包起来。3.2.4 异常与错误处理中的安全异常处理机制面向对象的异常处理机制:主要针对面向对象的语言,一般使用try-catch-finally结构来处理异常。面向过程的异常处理机制:对于一些非面向对象的语言,如VB,早期也具有异常处理机制,这就是面向过程的异常处理机制。VB.NET中,除了推出面向对象的异常处理机制外,也保留了面向过程的异常处理机制。3.2.4 异常与错误处理中的安全定义输入是一个广泛的概念 用户在软件上输入一个命令,进行相应操作
25、;用户输入自己的账号密码,进行登录验证;用户输入一个关键字,进行查询等。模块之间进行数据传递时,也会有相应输入,比如:一个模块调用另一个模块时,输入一些参数;一个模块读取一个配置文件,以对自己的行为进行配置等。3.2.5 输入安全输入的安全策略尽量让程序可以输入的入口少一些。尽量让输入所允许的输入类型少一些。严格检查不可信的输入。转变观念,从定义“非法”到定义“合法”。3.2.5 输入安全输入安全问题类别数字安全 数字格式、复负数验证、数值溢出字符串安全 使用正则表达式,应明确指出要匹配数据的开始;拒绝特殊字符环境变量安全 环境变量存储了和系统有关的一些配置信息;环境变量的存储格式给攻击者留下
26、机会文件名安全 默认文件名、限制使用特殊字符、禁止与物理设备文件名重复3.2.5 输入安全输入安全问题类别(续)数据库安全 包括表(Table)、视图(View)、存储过程(Stored Procedure)和触发器(Trigger)等的安全账户和口令安全问题 不使用管理员账户;不给予额外的权限;不允许使用空口令连接数据库;数据库连接字符串存放在配置文件中,加密保存。3.2.5 输入安全软件安全开发生命周期内存安全的重要性线程和进程的区别及安全异常和错误的安全处理输入安全的分类小结软件安全开发过程SSDL的组成部分获取安全需求的方法安全测试中的黑盒测试、白盒测试、灰盒测试软件安全部署的意义作业