<!---->1. <!---->深入剖析著名开源软件的质量问题<o:p></o:p>
<!---->2. <!---->全面阐述C、C++和Java代码中的常见编程错误<o:p></o:p>
<!---->3. <!---->指导你编写优秀代码的圣经<o:p></o:p>
更多详细信息:http://www.china-pub.com/37661<o:p></o:p>
3.1 脆弱代码
错误潜藏在周围各处,一有机会就偷偷潜入。任何方法都是不完美的。<o:p></o:p>
(Error is all around us and creeps in at the least opportunity. Every method is imperfect.)<o:p></o:p>
——查尔斯·尼科尔(Charles Nicolle),法国细菌学家,诺贝尔医学奖得主
<o:p> </o:p>
软件安全性是一个复杂又独特的难题。可以用最弱环现象(weakest-link phenomenon)<!---->[1]<!---->来描述软件安全性,不管你为系统的一部分提供的保护有多么完善,如果另一部分有安全漏洞,而你又碰上一个顽强的对手,那么你所有的努力都将付诸东流。相比之下,为满足其他非功能性软件需求,如为了让软件更加可移植、可靠、可用或者高效而采取的每个步骤,都将对最终的结果有着积极的作用。另外,只有根据需求才能正确判断安全性,在不同环境中需求的差别是很大的。同样的代码在某个环境中(例如,受防火墙保护的关系密切的用户团体)可能被认为是安全的,但是在另一个环境中(例如,互联网)则是不安全的。<o:p></o:p>
评估现有安全措施并规划新的安全措施时,正确的方法是进行安全专家们所说的风险分析(risk analysis):确定你想要保护的是什么,以及这些被保护的对象对你的重要性(你的资产以及它们的价值),找出你的资产所面临的风险,并对降低这些风险的各种方法进行评估。例如,本书就是你的资产之一,毫无疑问你认为这本书是一个非常宝贵的专业资源。本书面临的风险之一就是你把它借给一位同事,然后该同事相当自然地就忘记归还了。你可以拒绝出借本书以消除这种风险,但是这未免让你显得占有欲过强而且又过于猜疑;或者你可以将名字写在书的显著之处,这似乎是一个更好的方法。你拥有的另一项资产可能就是刚从饮水机里倒来的一杯水,可能会有人把它偷走。但是,因为这种事情发生的可能性很低,而且换杯水也是轻而易举的事,因此你的决定是接受这个风险,不采取任何措施。
评估一个系统时,解决各种复杂交互的典型方法是将注意力从产物转移到生产它的过程上。例如,新药的生产商不会呈交一瓶子药给监管局请求批文。实际上,药品公司需要提供各种文件,文件内容包括药品成分、研制过程以及严格书面证明的临床实验结果的细节。与此类似,为了判断软件的安全性,我们需要看到它的宿主环境的风险分析、综合需求、开发的详细过程以及完整的测试结果。<o:p></o:p>
上述各种因素的大部分都超出了本书的范围,在“进阶阅读”一节将列出一些很好的参考书目,对此这些书中有详细的介绍。在本章中,我们将局限在很窄的范围内,即给定一段源代码,我们要能够理解为满足典型安全需求所使用的一些重要实现模式与习惯用法,另外,同样重要的是还要能够嗅出危险的气味,即发现有可能导致安全漏洞的代码元素。因此,我们将首先研究哪些代码元素有益于我们的研究,然后转而关注一些普遍的代码安全问题:缓冲区溢出、竞态条件、问题API、不可信输入、结果验证以及数据泄漏和特权泄漏。最后,我们将讨论特洛伊木马代码以及能够帮助我们检查代码发现安全漏洞的工具。
<!---->[1]<!----> 即木桶效应,木桶装水的能力,取决于桶壁上最短的那块木板。——编者注
3.1 脆弱代码
在查找代码的漏洞时,忽略可以被攻击者任意修改和部署的代码(这种代码显然是不可信的),而将精力集中在可能会导致安全问题的代码,这样做可以提高效率。这样双管齐下,可以很快淘汰代码中不相干的绝大部分,把注意力集中在可能会被利用的有安全漏洞的代码上。在本节,我们将讨论如何分离出有危险的完整程序,而在3.7.4节中将研究如何分离出一个程序中的一部分。
<!----><!----><!---->让我们举一些例子来说明,在考虑了攻击者的能力的基础上如何缩小分析的范围。如果攻击者可以随便安装和运行应用程序(例如,正如一般Unix工作站上用户所能做的),那就没有必要对现有以普通用户权限运行的应用程序做脆弱性分析了,攻击者不大会在能够安装自己的攻击代码的情况下还费事去琢磨一个现有的应用。相反,如果有些应用程序是以特殊的权限(例如Unix环境中的setuid程序,或者在Windows系统中具有SeAssignPrimaryTokenPrivilege的程序)运行的,而攻击者缺乏安装此类程序的特权,那么这些程序就应该放在分析的范围之内。同样,从不可信的数据源接收数据的应用程序(例如,Web浏览器与邮件客户端)是应该做脆弱性分析的,因为攻击者有可能会利用这些程序来获得本地的访问权限,然后再利用其他漏洞来获得更高的特权。此外,如果攻击者可以物理地访问计算机并且安装不同版本的操作系统,那么针对该攻击者对操作系统源代码进行分析查找漏洞也就没有什么必要了(当然了,对于其他特权较低的攻击者而言该操作系统还是有漏洞的)。最后,考虑客户机/服务器应用,客户机被部署在不安全的计算机上,而服务器负责保护有价值的数据——这是很典型的情况。把某种攻击归咎于客户端程序的某个实现上的错误是没有诚意甚至是荒谬的,攻击者可以很轻松地将客户端程序用符合他们需要的某个程序替换。因此,客户端的代码不需要被考虑在安全性分析之列。以下摘自微软的安全建议的文字就是一个广为人知的例子,文中Samba CIFS客户端就被指责为对一个服务器的漏洞负有部分责任:<!---->[1]<!----> 它说明了在错误的地点实施安全策略所导致的一个安全问题。
微软(R)WinNews 电子简讯
特刊,1995年10月20日
[...]
Samba SMB客户端允许其用户通过网络发送非法的网络命令。
<!---->迄今为止,Samba客户端是唯一不过滤此类非法命令的SMB客户端。
[...]
使用更新后的驱动程序,SMBCLIENT用户将只能访问由Windows 95
<!----><!----><!---->用户指定的共享文件夹。
<!----><!----><!---->要做到将精力集中在可能导致安全问题的代码上会更困难,主要是因为代码之间的交互是非常微妙的,而忽视这些交互恰恰就是很多安全漏洞之后潜藏的典型模式。尽管如此,以下两个属性通常标示出潜在的脆弱代码。
(1) 这些代码运行时具有的特权级别比攻击者的要高。
(2) 攻击者可以输入数据给这些代码,或者以其他方式来操作这些代码。
请注意,将这些代码放在防火墙或者安全的Web服务器之后并不能解决问题,只要不可信的数据能够输入给这些代码,就有安全性问题。
<!----><!----><!---->举例来说,如果有人试图通过互联网来攻击某台计算机,那么所有接收网络连接的程序(以及所有这些程序运行的其他程序)所包含的代码都应该接受安全漏洞检查。可以使用netstat –a命令(在Unix与Windows系统上均可用)来列出特定主机上接收来自互联网的连接的IP端口列表。表3-1和表3-2分别列出了在一台Windows工作站与一台Unix服务器上运行netstat命令的输出结果。在Unix系统上,fstat<!---->[2]<!---->与lsof <!---->[3]<!---->程序可以用于将这些端口名映射到对应的程序。在Windows XP系统上,netstat的–o选项会列出在每个端口上监听的进程的进程ID,这些进程ID则可以进一步被用于映射到进程所对应的程序的名字,只要检查tasklist命令输出的任务列表即可。使用Process Explorer<!---->[4]<!---->应用程序可以获得更多的细节信息。
<!----><!----><!----><!----><!----><!---->本地用户也可以利用代码的漏洞来获取更高级别的特权。Unix系统支持标记为setuid与setgid的文件,这种文件的代码执行时所具有的特权级别是由文件的属主或者所属组决定的。与此类似,在Windows系统中,一个对象(例如可执行文件或者服务)可以以某个特定用户、用户组或者安全主体的有效许可来运行,前提是具有适当权限的进程使用诸如CreateProcessAsUser的调用来激活它。这些程序中存在的安全漏洞会让攻击者获得对应用户或者用户组的特权。因此,这种程序也是很容易受到本地攻击的,具有在特定主机上执行程序的权限的用户能够利用这些程序的漏洞来获得进一步的权限。请注意有些攻击所做的是连续特权提升(privilege escalation):远程用户首先利用某个网络程序的漏洞获得对计算机的普通用户访问权限,然后基于这种访问权限利用某个在更高权限级别上运行的程序的漏洞获取更多的特权。请记住在某些情况下,一个网络程序可能会利用setuid机制,以更低的特权级别来运行,将攻击成功对系统带来的伤害降到最低。在Unix主机上,可以使用如下的命令获得以不同的特权级别运行的程序的列表:<!---->[5]<!----><o:p></o:p>
<!----><!----><!----><!----><!----><!---->
<!----><!----><!---->另外,请注意典型的操作系统内核完全控制着特权管理。因此,内核代码总是会受到本地与远程攻击。在大多数情况下,内核代码还包含设备驱动程序与可加载到内核的模块。
表3-1 Unix服务器上开放的网络端口列表<o:p></o:p>
当前的网络连接(包括服务器)<o:p></o:p> |
|||||
<!----><!----><!---->协议<o:p></o:p> tcp4<o:p></o:p> tcp4<o:p></o:p> tcp4<o:p></o:p> tcp4<o:p></o:p> tcp4<o:p></o:p> tcp4<o:p></o:p> tcp4<o:p></o:p> udp4<o:p></o:p> udp4<o:p></o:p> udp4<o:p></o:p> udp4<o:p></o:p> udp4<o:p></o:p> udp4<o:p></o:p> udp4<o:p></o:p> udp4<o:p></o:p> |
接收队列<o:p></o:p> 0<o:p></o:p> 0<o:p></o:p> 0<o:p></o:p> 0<o:p></o:p> 0<o:p></o:p> 0<o:p></o:p> 0<o:p></o:p> 0<o:p></o:p> 0<o:p></o:p> 0<o:p></o:p> 0<o:p></o:p> 0<o:p></o:p> 0<o:p></o:p> 0<o:p></o:p> 0<o:p></o:p> |
发送队列<o:p></o:p> 0<o:p></o:p> 0<o:p></o:p> 0<o:p></o:p> 0<o:p></o:p> 0<o:p></o:p> 0<o:p></o:p> 0<o:p></o:p> 0<o:p></o:p> 0<o:p></o:p> 0<o:p></o:p> 0<o:p></o:p> 0<o:p></o:p> 0<o:p></o:p> 0<o:p></o:p> 0<o:p></o:p> |
本地地址<o:p></o:p> *.http<o:p></o:p> *.submission<o:p></o:p> *.smtp<o:p></o:p> *.ssh<o:p></o:p> *.imaps<o:p></o:p> localhost.domain<o:p></o:p> istlab.domain<o:p></o:p> *.ntalk<o:p></o:p> localhost.ntp<o:p></o:p> istlab.ntp<o:p></o:p> *.ntp<o:p></o:p> *.1024<o:p></o:p> localhost.domain<o:p></o:p> istlab.domain<o:p></o:p> *.syslog<o:p></o:p> |
远程地址<o:p></o:p> *.*<o:p></o:p> *.*<o:p></o:p> *.*<o:p></o:p> *.*<o:p></o:p> *.*<o:p></o:p> *.*<o:p></o:p> *.*<o:p></o:p> *.*<o:p></o:p> *.*<o:p></o:p> *.*<o:p></o:p> *.*<o:p></o:p> *.*<o:p></o:p> *.*<o:p></o:p> *.*<o:p></o:p> *.*<o:p></o:p> |
状态<o:p></o:p> LISTEN<o:p></o:p> LISTEN<o:p></o:p> LISTEN<o:p></o:p> LISTEN<o:p></o:p> LISTEN<o:p></o:p> LISTEN<o:p></o:p> LISTEN<o:p></o:p> |
最后,需要提醒的是成功的攻击并不一定做了特权提升。有些攻击者可能会发动拒绝服务(denial-of-service,DoS)攻击,让某个系统或者某项服务不可用。任何通过网络提供服务的程序,如果在收到恶意编排的数据时会崩溃,那么这种攻击对于这个程序来说就是危险的。
表3-2 微软Windows工作站上开放的网络端口列表<o:p></o:p>
当前的网络连接<o:p></o:p> |
|
<!----><!----><!---->协议<o:p></o:p> TCP<o:p></o:p> TCP<o:p></o:p> TCP<o:p></o:p> TCP<o:p></o:p> TCP<o:p></o:p> TCP<o:p></o:p> TCP<o:p></o:p> TCP<o:p></o:p> UDP<o:p></o:p> UDP<o:p></o:p> UDP<o:p></o:p> UDP<o:p></o:p> UDP<o:p></o:p> UDP<o:p></o:p> UDP<o:p></o:p> UDP<o:p></o:p> UDP<o:p></o:p> |
本地地址<o:p></o:p> eagle:epmap<o:p></o:p> eagle:microsoft-ds<o:p></o:p> eagle:3891<o:p></o:p> eagle:netbios-ssn<o:p></o:p> eagle:1277<o:p></o:p> eagle:1296<o:p></o:p> eagle:1359<o:p></o:p> eagle:netbios-ssn<o:p></o:p> eagle:microsoft-ds<o:p></o:p> eagle:isakmp<o:p></o:p> eagle:netbios-ns<o:p></o:p> eagle:netbios-dgm<o:p></o:p> eagle:1900<o:p></o:p> eagle:ntp<o:p></o:p> eagle:netbios-ns<o:p></o:p> eagle:netbios-dgm<o:p></o:p> |