作者:Michael Howard
相关技术:Windows Server 2003、Windows XP、安全性
[摘要]介绍了如何在应用系统中标识和减小攻击面,减少在默认情况下执行的代码数量,同时也介绍了在黑客攻击代码时如何限制伤害程度。
代码执行失败,这是程序生活中令人悲痛的事情。但也正是这些安全故障和代码漏洞引起了我的浓厚兴趣。在这个行业中,我们为提高代码质量付出了大量的心血。尽管代码质量非常重要,但大多数代码最终都将执行失败,因此我们不能只关注完善代码正确性的问题。暂时假定您的代码是完美的,它也只是按照目前的标准而言是完美的——它只反映了在开发时的最佳做法。然而,漏洞研究领域在不断地发展。四年前,整数溢出攻击几乎闻所未闻,但现在它们俨然已成为常见的攻击手段!设想一下将这一范围扩大到您曾经交付给客户的所有代码中。您需要将观点从“我的代码质量非常好”转变为“虽然以目前的知识来看,我的代码是最好的,但它仍可能存在安全缺陷”。一旦您进入这一思想境界,攻击面减少(Attack Surface Reduction,ASR)的基本原理就变得非常容易领会了。应用程序的攻击面是所有用户都可以使用的代码、接口、服务、协议和准则的并集,并侧重于未经身份验证的用户可以访问的内容。ASR的核心原则是所有代码都可能具有一个或多个漏洞,且某些漏洞会导致客户的利益受到损害。因此,确保客户利益不会受到损害的唯一方式就是将代码的使用率降低为零。考虑到安全和未知的风险,ASR是理想的折衷方案——它将暴露在不受信任用户面前的代码减少到最低限度。代码质量与攻击面减少相结合,可以帮助您生成更安全的软件——完美的代码无法单独达到这一目标。请记住,永远不要忘记对您的客户的追踪服务:他们需要应用您的所有安全更新方案和策略。如果代码有缺陷,那么您的客户是必须使用不完善的功能来修补4台计算机呢,还是默认的400台计算机呢?对于处理这一问题的人而言,这会产生巨大的差异。
在本文中,我将说明一个简单的过程,以便帮助您减少攻击面,并帮助您提高应用程序的安全性。ASR有三个主要目标:
将80/20规则应用于所有功能领域。80%的用户使用该功能吗?如果不是,请考虑关闭该功能。
此举相对容易——只需关闭一些功能即可!应用80/20规则,并问自己一下:“我的80%的客户使用该功能吗?”如果答案是否定的,则将其关闭并使其很容易重新打开。在Windows Server 2003中,我们关闭了20个以上的服务功能,因为如果服务不运行,它就不会受到攻击。例如,默认情况下不会安装Web服务器IIS 6.0,如果您确实安装了它,则默认情况下它只提供静态文件,所有形式的动态Web内容都是可选择性加入的。举个例子来说,如果在WebDAV中存在安全缺陷,则受到影响的客户只是那些频繁使用IIS和WebDAV的客户,而不是Windows Server 2003的所有用户。Microsoft安全公告MS04 020“Vulnerability in POSIX Could Allow Code Execution”中描述的另一个示例则不会影响Windows XP或Windows Server 2003,应用80/20规则,80%的用户不使用POSIX子系统(坦白地说,在我知道的用户中没有任何人使用它),所以它被简单地从该操作系统中移除。显然,关闭部分应用服务功能可能会导致原来能够正确操作的应用程序失败,并且当人们想要使用该功能时,可能会增加复杂性。现在我是一名安全工作人员,因此我总是侧重于从安全的角度考虑问题,但是我也痛苦地知道当应用程序失败时对非技术用户所造成的影响。您可以通过永远不运行代码来生成一个极为安全的系统,但是那不会非常有用!好消息是,ASR不仅仅是一个“打开或关闭”建议,它可以通过限制在代码运行后可以访问它的用户的权限来减少攻击面。这会导致一个有趣的副作用:许多功能仍然可用,但是攻击者无法访问。
回到IIS示例,限制可访问指定Web站点上的WebDAV功能的另一个方式是:使用WebDAV限制对任何站点的访问。默认情况下,大多数Web站点都可供所有用户访问(不管他们有什么意图),因此Internet上的任何攻击者都可能利用此缺陷。但是,如果Web服务器将访问权限只授予受信任的用户或特定子网上的用户,则只有较少的用户可以利用该缺陷。这样做很好,因为代码可以为那些需要该功能的用户工作。我最喜欢的示例之一是Windows XP Service Pack 2中的防火墙,当我们打开该防火墙时,一个很常见的方案崩溃了:小型家庭网络(其中,一台中心计算机充当家庭中其他计算机的文件和打印服务器)。通过在防火墙中关闭端口来禁用该功能是一项不可行的选择,因为这可能会损害成百上千个用户的利益。折衷的方案是打开这些端口,但是只在用户的本地子网上这样做。合法用户可以访问文件并打印它们,但是那些坏家伙则无法这么做(有关Service Pack 2的详细信息,请参阅补充文章《Windows XP Service Pack 2:Information for Developers》,参见http://msdn.microsoft.com/msdnmag/issues/04/11/AttackSurface/default.aspx?side=true#a)。另一个减少可与可能存在漏洞的代码进行通信的人员数量的方式是,在调用方访问代码之前对其进行身份验证(当然攻击者也将尝试攻击身份验证代码,但这个问题留待以后讨论),我们在Windows XP SP2中就是这样做的。我们将对远程过程调用(RPC)终结点的访问权限授予合法的Windows用户,而匿名用户(可能是坏家伙)则无法访问该终结点,因而也无法攻击代码。请记住,大多数坏家伙都没有经过身份验证,并且通过稍微提高一些身份验证的门槛,就可以消除大量潜在的攻击者的威胁。对于我安装的每台Web服务器,我都要做这件事情——我所禁用的首要设置之一就是匿名访问。
让我们暂时假设您的代码没有漏洞,并且您决定不使用我刚刚建议的某些简单措施来限制对代码的访问。您仍然可以通过用减少的特权操作代码来降低损害可能性。让我们考察另一个示例:在2004年6月8日,Microsoft发布了一个影响业务对象中的Crystal Reports的安全公告(Crystal Reports Web Viewer Security Update for June 2004)。该技术公告提供了以下警告:
能够成功利用漏洞的攻击者可以通过受影响系统上的Crystal Reports和Crystal Enterprise Web查看器来检索和删除文件。受到该漏洞影响的文件的数量取决于Crystal Web查看器所使用的受影响组件的安全上下文。
因为ASP.NET工作进程在默认情况下以较低权限的ASPNET帐户运行,并且不是管理员帐户(例如,SYSTEM),所以损害被限制为读取文件系统中的文件,而不是删除它们,这是在ASP.NET于2003年2月最初发布之前进行的一处更改。当然,这仍然是一个糟糕的程序缺陷:攻击者可以读取您的BOOT.INI文件,但起码他们无法删除该文件。该程序的另一缺陷是攻击者还可以读取您的C:\WINDOWS\repair\sam文件,或者在Unix或Linux上,可以读取您的/etc/passwd文件(该程序缺陷会影响所有运行Crystal Reports的平台,包括AIX、Solaris和Linux)。现在,坏家伙可以尝试进行脱机密码破解攻击。如果您的代码必须用较高的特权进行操作,请移除应用程序不明确需要的其他特权。例如,我正在开发的工具需要一个“危险”的Windows特权——备份特权。这很可怕,因为在具有该特权的帐户下运行的任何进程都可以读取文件系统中的任何文件,而不管文件上的访问控制列表(ACL)是如何设定的。这就是为什么它被称为备份特权的原因——您不会希望备份应用程序因为存在ACL冲突而只备份某些文件,却不备份其他文件。有两个解决该问题的办法:可以用SYSTEM帐户(它具有特权)运行该服务,或者可以在只具有该特权的特定帐户下运行该服务。SYSTEM的问题在于它具有备份特权,同时它还具有还原特权、调试特权以及“充当操作系统的一部分”的特权,并且它还是管理帐户——我想您可以猜到我的选择。除非您已经尝试了所有其他可能,否则您永远都不应当用SYSTEM帐户、作为root帐户的守护程序,或使用具有管理权限的用户帐户来运行服务,代码中的缺陷可能会导致灾难性的故障。将默认帐户设置成较低的权限,并且如果边界方案不能工作,请让管理员知道哪些方案执行失败,并允许他进行更改(如果他认为利益胜过风险的话)。当人们考虑到减少攻击面时,他们通常会想到安全设计——这是不错的——但是开发人员(而不仅仅是设计人员和架构师)也可以通过分析匿名代码路径来帮助推动这一过程。
威胁到建模过程的操作涉及到生成数据流关系图(Data Flow Diagrams, DFDs),或者使用类似的技术(例如,统一建模语言[UML]交互关系图)来标识应用程序的入口点——正是这些入口点可能被攻击者访问到。作为开发人员,您首先需要确保代码的所有入口点都整合到了DFD中。接下来,您应当考察每个入口点,以确定访问该入口点所必需的特权。然后,通过DFD进行跟踪,以寻找代码可以接触到的所有潜在的数据流、数据存储和进程——或者更准确地说,寻找攻击者可以接触到的代码。让我们假设代码只具有两个面向网络的入口点:一个是匿名的,另一个只能由管理员访问——因为代码会在用户可以继续执行之前对他们进行身份验证。现在请考虑一下:坏家伙将要从哪里攻击?他将沿着匿名数据路径(也称为威胁路径)来攻击代码,原因只是在于他能够这样做。他不会尝试沿着管理代码路径进行攻击,因为如果他能这么做,那么他俨然就是管理员了。
Microsoft在2004年早些时候发布了一个影响Windows 2000、Windows XP和Windows Server 2003的安全公告,所修复的缺陷之一位于专用通信传输(Private Communications Transport,PCT)协议中,该协议是Microsoft安全套接字层(Secure Sockets Layer,SSL)库的一部分。过去,PCT曾在美国联邦加密输出限制政策存在期间使用,现在没有这样的限制,因此PCT已经不常用了。这就是为什么在Windows Server 2003中完全禁用PCT的原因,并且,因为该程序缺陷位于PCT中,所以Windows Server 2003在默认情况下不受影响——即使该缺陷位于代码中。但是,Windows 2000会受到影响,因为默认情况下在启用SSL的Windows 2000 Web服务器上会启用PCT,所以对于它们而言,该修复程序非常关键。另一个需要提防的协议是用户数据报协议(User Datagram Protocol, UDP)。非常有趣的一件事情是,许多使用TCP的应用程序还侦听UDP通信,尽管它们不需要这样做。如果您的应用程序只使用TCP,请不要侦听UDP通信。还记得Slammer吗?Slammer是一种有效的蠕虫病毒,因为有效负载恰好可以填满一个UDP数据包。因为不像TCP中那样有三次握手,所以蠕虫病毒可以非常迅速地传播。有关对该蠕虫影响进行的有趣分析,请参阅《Analysis of the Sapphire Worm》(参见http://www.caida.org/analysis/security/sapphire)。
此外,确实非常重要的一点是,您应该在设计过程中尽可能早地减少攻击面。
等待至产品开发过程的后期才减少攻击面通常是非常困难的,这是因为在后期更改功能会导致回归错误。其他功能可能依赖于这个以特定方式工作的功能,而您却恰恰更改了该功能!当我们减少Windows Server 2003中Microsoft Internet Explorer的攻击面时,我们遇到了类似的问题;Windows Update停止了工作,因此我们不得不进行一些小的更改,以便使其能够工作。在设计应用程序时,您应当在设计中安排一个部分,以便概述攻击面将具有的外观。您应当标识如下项目:
如果开发人员较早知道攻击面的外观,他们可以从一开始就相应地设计和编码,而不是等到最后才出现令人讨厌的意外。等待太长时间会导致巨大的代码动荡和糟糕的体验,因为必须对设计进行更改以适应干扰。
随着开发周期中的每个阶段不断逝去,您应当衡量产品的攻击面。开始时使用一个基准,然后每周都使用各种扫描工具来统计在上一个阶段中标识的所有项目。在某些情况下,如果您具有特定于您的应用程序的入口点,则可能需要编写自己的工具。如果攻击面统计结果上升,请确定其原因,并查看您是否可以使其下降。当工程师们知道您正在衡量攻击面时,他们会首先努力减少攻击面而不是置身事外。有关示例,请参见图1。
基准时间 | 基准时间 + 1个月 | 注解 |
3个TCP端口 | 2个TCP端口 | 很好,可以少为一个端口操心了。 |
1个UDP端口 | 2个UDP端口 | 哪个功能开启了新的UDP端口?为什么它默认打开了这个端口?它经过验证了吗?它被限制到了一个子网中了吗? |
2个服务(都是SYSTEM) | 3个服务(2个SYSTEM,1个LocalService) | 为什么默认运行了另一个服务?究竟是哪两个作为SYSTEM运行? |
3个ActiveX控件 | 4个ActiveX控件 | 为什么安置了一个新的控件?其代码是安全的吗? |
无附加用户帐户 | 1个应用程序帐户 | 先把它从Administrators组中移除!然后再考虑为什么(会出现这样一个帐户)?密码是什么? |
图1 与基准时间比较暴露的攻击表面
在完成上述所有工作之后,如果您确定您依旧必须交付具有较大攻击面的应用程序,那么这显然很糟糕。这不仅意味着客户在默认情况下可能受到攻击,而且意味着您有大量代码需要审查。我喜欢这样看待该问题:较大的攻击面意味着您对代码的质量充满信心。您能够信任代码的唯一方式是进行非常广泛的代码审查,并确保代码是防御性的和保守的。假设有两个类似的产品,一个由您自己生产,另一个由您的竞争对手生产。竞争对手的产品在默认情况下不会运行,而当它运行时,会使用最小的特权运行,并且只打开一个TCP套接字——默认情况下,它被限制到本地子网。而您的产品在默认情况下以SYSTEM身份运行,并打开一个UDP套接字和三个TCP套接字,并且它们全都没有经过身份验证且面向Internet。现在来了一个攻击者——他将攻击哪个系统?我将给您一个提示:您的系统!如果您的代码中有缺陷,则很可能会危及运行您代码的服务器的安全。更严重的是,他还可以攻击您的其他客户,因为他们在默认情况下都允许该功能运行。最后,让用户和管理员知道您所做的事情。记录受到减少的攻击面影响的功能,并且说明在遇到由这些设置而产生的问题时,客户可以如何调整系统。在您的建议中要保守一些,并确保客户知道启用某个端口或功能的含义。如果您的应用程序依赖防火墙,并且用户希望使用特定端口访问某个功能,那么请一定告诉他们要打开哪个端口;无论如何都不要告诉他们关闭防火墙!
Michael Howard是一位高级程序经理,他是Microsoft Secure Windows Initiative小组的创始人之一,并与他人合著了《Writing Secure Code 》(Microsoft Press, 2002)一书。他专门设计、生成、测试和部署能够抵御攻击、同时仍然对数百万非技术用户有用的应用程序。