现在,让我们使用上文提到的所有项目设计一个场景,通过实际演练来了解善意程序造成的恶劣后果。本章开始提到的电子邮件程序就是个好例子。稍后,我们将按照SDLC的所有步骤,来看看是哪里会出现问题。1)概念和方案:XYZ公司认为,如果他们使用比市面上任何其他程序都好的特定程序发送和管理电子邮件,是一件很不错的事情。该特定程序将使用经特殊改写的格式,在消息中提供独一无二的功能。会出现什么问题呢?它将会使系统更加复杂。跟GDI+ API例子类似,更多的复杂性带来更多的故障。如果新功能对该产品的成功非常重要,那么从项目一开始,就应该重视系统中特定部分的安全性。至少,关键组件的可靠性和安全性应该是概念中的一项重要内容。2)需求收集:从提出概念的人那里收集详细的功能需求。我们对所有将采用的功能设计及其能够对业务模型起到的提升作用都感兴趣,因为它们可能会在将来的某个时刻起到重大作用。会出现什么问题呢?没有一项需求包含有关安全性和可靠性的内容。而提供安全性和可靠性应该是任何应用需求的一部分。来自需求收集过程中的每个想法都将导致某种程度的项目需求变更,这将引发功能性的设计缺陷。在需求阶段,限定需求的范围,除了能够使成本最小化且确保时间进度外,还能最小化漏洞产生的几率。3)设计:应用程序的细节,包括数据库、文件系统、操作系统、兼容性,都需要仔细设计。会出现什么问题呢?设计,像需求分析一样,只考虑功能,所有的注意力都集中在如何使应用程序工作上。应该将一部分工作优先放在确保应用程序创建严格符合限定条件的数据流上,然后对输入/输出数据的有效性进行验证,这样就可以较好地保证输入的应用程序函数参数的合法性。进程间通信必须设有数据有效性检测和权限授予的代码段,以使进程能够验证来自另一个进程数据有效性和进程来源的合法性及操作权限。从这个阶段开始应该引入威胁建模分析。通过识别程序可能遭受攻击的高危部分,开发团队可以集中精力来改进各个组件,尤其是最易受到攻击的组件。4)编码:完成设计之后,开始编写代码。会出现什么问题呢?在漏洞世界里众所周知的是:程序开发人员并不都愿意遵照那些能够避免类似缓冲区溢出等漏洞的优化编码方案。但是,应该尽量使用最佳安全编码方案,例如动态调整的数据结构的边界检查(bound checking)和输入数据的有效性检查。5)测试:测试的频率很高。故障不断被发现并被迅速修复。测试员不断对所有未包括安全组件的功能需求项进行验证以确保它们的正常实现。会出现什么问题呢?软件测试一般承受着最大的时间压力,因为管理者希望尽快生产出成品,并且几乎不能容忍丝毫的延迟。开发人员常常将程序分解以期尽快通过测试阶段,因为每当这样做时,测试人员的注意力就会全部集中在功能上而跳过回归测试(regression test)。应该参照前文提到过的威胁模型分析来对产品进行某些渗透测试。6)部署:最终产品出厂了,经过包装被运送到世界各地毫无戒心的顾客手中,附带软件的安装使用说明书。会出现什么问题呢?说明书中没有对软件运行最合适、最安全的系统配置做任何说明,也没有指出在网络接口上哪些网络端口是除了服务器外对其他任何IP地址都不能开放的。开发团队应该制定一些策略来处理发现的漏洞。如此一来,就需要更多的测试人员来对软件进行渗透测试了。7)漏洞检测:一个对该产品感兴趣的用户可能会好奇,如果利用这个电子邮件软件发送一封经过特殊设计的电子邮件会产生什么情况。这封特殊的邮件能够满足软件特定模块的处理要求,但是,其消息数据超出了该模块标准数据结构的要求。结果是程序崩溃了,并在操作系统的内存中留下一些消息碎片。8)漏洞利用:现在,该用户想知道是否能在这封特殊邮件的消息内容中嵌入一个程序以使其能够留在内存中并运行。结果发现这确实可行。在实验中,一个小的“hello world”程序运行起来了。9)漏洞修复:此刻这个非常激动的用户开发了一个增强版本,在邮件消息中嵌入的程序能够生成邮件自身的多份副本,并自动将其发送给通讯录中的其他收件人。电子邮件程序强大的功能设计使得访问邮件通讯录变得更为容易了。就这样,该产品的首个电子邮件蠕虫诞生了。建议:应该尽早解决能够被利用的漏洞。从长远来看,在达到相同安全等级的情况下,这样做能有效减少技术上和安全上的工作量,并能减少检测和防范工具的开销。