核心防御机制

Web应用程序的基本安全问题(所有用户输入都不可信)致使应用程序实施大量安全机制来抵御攻击。尽管其设计细节与执行效率可能千差万别,但几乎所有应用程序采用的安全机制在概念上都具有相似性。

Web应用程序采用的防御机制由以下几个核心因素构成。

处理用户访问应用程序的数据与功能,防止用户获得未授权访问。

处理用户对应用程序功能的输入,防止错误输入造成不良行为。

防范攻击者,确保应用程序在成为直接攻击目标时能够正常运转,并采取适当的防御与攻击措施挫败攻击者。

管理应用程序本身,帮助管理员监控其行为,配置其功能。

鉴于它们在解决核心安全问题过程中所发挥的重要作用,一个典型应用程序的绝大多数受攻击面[1]也由这些机制构成。知己知彼是战争的首要法则,那么防御攻击者向应用程序发动有效攻击的重要前提是彻底了解这些机制。无论读者在渗透测试方面是否有经验,都应花时间了解这些核心机制在遇到的每一种应用程序中的工作原理,并确定使其易于受到攻击的弱点。

处理用户访问

几乎任何应用程序都必须满足一个中心安全要求,即处理用户访问其数据与功能。在通常情况下,用户一般分为几种类型,如匿名用户、正常通过验证的用户和管理用户。而且,许多情况下,不同的用户只允许访问不同的数据,例如,Web邮件应用程序的用户只能阅读自己的而非他人的电子邮件。

大多数Web应用程序使用三层相互关联的安全机制处理用户访问:

身份验证;

会话管理;

访问控制。

上述每一个机制都是应用程序受攻击面的一个关键部分,对于应用程序的总体安全状况极其重要。由于这些机制相互依赖,因此根本不能提供强大的总体安全保护,任何一个部分存在缺陷都可能使攻击者自由访问应用程序的功能与数据。

身份验证

从理论上说,身份验证机制是应用程序处理用户访问的最基本机制。验证用户是指确定用户的真实身份。如果不采用这个机制,应用程序会将所有用户作为匿名用户对待,这是最低一级的信任。

今天,绝大多数Web应用程序都采用传统的身份验证模型,即要求用户提交用户名与密码,再由应用程序对其进行核实,确认其合法性。一种典型的登录功能如图2-1所示。在安全性至关重要的应用程序(如电子银行使用的应用程序)中,通常使用其他证书与多阶段登录过程强化这个基本模型。在安全要求更高的情况下,可能需要基于客户端证书、智能卡或质询-响应令牌(challenge-response token)使用其他身份验证模型。除核心登录过程外,身份验证机制往往还要采取一系列其他支持功能,如自我注册、账户恢复和密码修改工具。

图2-1 一种典型的登录功能

尽管表面看似简单,但无论是设计方面还是执行方面,身份验证机制都存在大量缺陷。常见的问题可能使得攻击者能够确定其他用户的用户名、推测出他们的密码,或者利用其逻辑缺陷完全避开登录功能。攻击Web应用程序时,渗透测试员应当投入大量精力,攻击应用程序采用的各种与身份验证有关的功能。出人意料的是,这种功能中存在的缺陷往往允许攻击者非法访问敏感数据与功能。

会话管理

处理用户访问的下一项逻辑任务是管理通过验证用户的会话。成功登录应用程序后,用户会访问各种页面与功能,从浏览器提出一系列HTTP请求。与此同时,应用程序还会收到各类用户(包括通过验证的用户与匿名用户)发出的无数请求。为实施有效的访问控制,应用程序需要识别并处理每一名用户提交的各种请求。

为满足以上要求,几乎所有的Web应用程序都为每一位用户建立一个会话,并向用户发布一个标识会话的令牌。会话本身是一组保存在服务器上的数据结构,用于追踪用户与应用程序的交互状态。令牌是一个唯一的字符串,应用程序将其映射到会话中。当用户收到一个令牌时,浏览器会在随后的HTTP请求中将它返回给服务器,帮助应用程序将请求与该用户联系起来。虽然许多应用程序使用隐藏表单字段(hidden form field)或URL查询字符串(query string)传送会话令牌(session token),但HTTP cookie才是实现这一目的的常规方法。如果用户在一段时间内没有发出请求,会话将会自动终止,如图2-2所示。

图2-2 一个实施会话超时的应用程序

就受攻击面而言,会话管理机制的有效性基本上取决于其令牌的安全性,绝大多数针对它的攻击都企图攻破其他用户的令牌。如果令牌被攻破,攻击者就可以伪装成被攻破的用户,像已经通过验证的用户一样使用应用程序。令牌生成过程中存在的缺陷是主要的漏洞来源,使攻击者能够推测出发布给其他用户的令牌;随后,攻击者再利用令牌中的缺陷截获其他用户的令牌。

少数应用程序不向用户发布会话令牌,而是通过其他方法在多个请求中重复确认用户身份。如果使用HTTP的内置身份验证机制,那么浏览器会自动在每个请求中重复提交用户证书,帮助应用程序直接通过这些请求识别用户。在其他情况下,应用程序会将状态信息保存在客户端而非服务器上,通常还需要对这些信息进行加密,以防遭到破坏。

访问控制

处理用户访问的最后一个逻辑步骤是做出并实施正确的决策,决定允许或拒绝每一个请求。如果前面的机制运作正常,应用程序即可从收到的每一个请求确认用户的身份。在此基础上,应用程序需要决定是否授权用户执行其所请求的操作或访问相关数据(见图2-3)。

图2-3 应用程序正实施访问控制

访问控制机制一般需要实现某种精心设计的逻辑,并分别考虑各种相关应用程序领域与不同类型的功能。应用程序可支持无数不同的用户角色,每种角色都拥有特定的权限,每名用户只允许访问应用程序中的部分数据。应用程序可能需要根据用户的身份,通过特殊功能实现交易限制与其他检查。

由于典型访问控制的要求相当复杂,因此这种机制中一般存在大量的安全漏洞,使得攻击者能够未授权访问应用程序的数据与功能。开发者经常会对用户与应用程序的交互方式做出错误假设,并常常会有所疏忽,在某些应用程序功能中省略访问控制检查。探查这些漏洞是一件费力的工作,因为需要对每一项功能重复进行相同的检查。然而,因为访问控制机制中存在大量漏洞,所以在测试Web应用程序时付出这样的努力总是值得的。第8章我们会讲述在执行严格的访问控制试测时,如何将某些操作自动化。

处理用户输入

回想一下第1章描述的基本安全问题:所有用户输入都不可信。大量针对Web应用程序的不同攻击都与提交错误输入有关,攻击者专门设计这类输入,以引发应用程序设计者无法预料的行为。因此,能够安全处理用户输入是对应用程序安全防御的一个关键要求。

应用程序的每一项功能以及几乎每一种常用的技术都可能出现输入方面的漏洞。通常来说,输入确认(input validation)是防御这些攻击的必要手段。然而,任何一种保护机制都不是万能的,防御恶意输入也并非如听起来那样简单。

输入的多样性

典型的Web应用程序以各种不同的形式处理用户提交的数据。一些类型的输入确认可能并不适用或能够确认所有这些形式的输入。通常由用户注册功能执行的输入确认如图2-4所示。

图2-4 应用程序正执行输入确认

在许多情况下,应用程序可能会对一些特殊的输入实行非常严格的确认检查。例如,提交给登录功能的用户名的最大长度为8个字符,且只能包含字母。

在其他情况下,应用程序必须接受更广泛的输入。例如,提交给个人信息页面的地址字段可合法包含字母、数字、空格、连字符、撇号与其他字符。但是仍然可以对这个字段实施有效的限制。例如,提交的数据不得超过某个适当的长度限制(如50个字符),并不得包含任何HTML标记(HTMLmarkup)。

有些时候,应用程序可能需要接受用户提交的任意输入。例如,一名博客应用程序用户可以建立一个主题为“攻击Web应用程序”的博客。博客文章和评论可合法包含所讨论的明确攻击字符串。应用程序可能需要将这些输入保存在数据库中,写入磁盘,并以安全的方式向用户显示。不能仅仅因为输入看似恶意(但并未显著破坏应用程序对一些用户的价值),就拒绝接受该输入。

除了用户通过浏览器界面提交的各种输入外,一个典型的应用程序还会收到大量数据,它们在服务器上生成,并被传送给客户端,以便客户端能够在随后的请求中将其返回给服务器。这些数据包括cookie和隐藏表单字段,普通应用程序用户虽然无法浏览这些数据项,但攻击者能够查看并修改它们。在这些情况下,应用程序通常可对接收到的数据执行非常特殊的确认操作。例如,一个参数可能必须包含一个特殊的已知值(如说明用户首选语言的cookie),或者为某种特殊的格式(如一个顾客的身份证号码)。而且,如果应用程序发现服务器上生成的数据遭到修改,并且使用标准浏览器的普通用户根本不可能进行此类修改,那么极有可能是该用户正企图探查应用程序的漏洞。在这些情况下,应用程序应拒绝该用户提交的请求,并将事件记入日志文件中,以便随后进行调查(请参阅2.3节了解相关内容)。

输入处理方法

通常可采用各种方法来处理用户输入。不同的方法一般适用于不同的情形与不同类型的输入,有时最好结合采用几种方法。

拒绝已知的不良输入

这种方法一般使用一个黑名单,其中包含一组在攻击中使用的已知的字面量字符串或模式。确认机制阻止任何与黑名单匹配的数据,并接受其他数据。

一般来说,因为两方面的主要原因,这种方法是确认用户输入效率最低的方法。首先,攻击者可通过一系列输入对典型Web应用程序中存在的漏洞加以利用,这些输入可通过各种方式进行编码,或者表现为不同的形式。除非在最简单的情况下,否则,黑名单可能会忽略某些可用于攻击应用程序的输入模式。其次,攻击技术处在不断发展的过程之中。当前的黑名单无法防止利用现有漏洞的新型方法。

通过对被阻止的输入稍做调整,即可轻易避开许多基于黑名单的过滤。例如:

如果SELECT被阻止,则尝试SeLeCt;

如果or 1 = 1 -- 被阻止,则尝试or 2 = 2 --;

如果alert('xss')被阻止,则尝试prompt('xss')。

在其他情况下,通过在表达式之间使用非标准字符破坏应用程序执行的令牌,可以避开旨在阻止特定关键字的过滤。例如:

最后,各种基于黑名单的过滤,特别是那些由Web应用程序防火墙执行的过滤,都易受空字节攻击。由于在托管和非托管情况下处理字符串的方式各不相同,在被阻止的表达式之前的任何位置插入空字节可能导致某些过滤器停止处理输入,并因此无法确定表达式。例如:

我们将在第18章介绍各种攻击Web应用程序防火墙的其他技巧。

 注解 对空字节的处理方式加以利用的攻击存在于Web应用程序安全的各个领域。在空字节被当做字符串分隔符的情况下,空字节可用于终止文件名或对某个后端组件的查询。在接受并忽略空字节的情况下(例如,在某些浏览器的HTML代码中),可以在被阻止的表达式中插入任意空字节,以避开基于黑名单的过滤。这类攻击将在后面几章详细介绍。

接受已知的正常输入

这种方法使用一个白名单,其中包含仅与良性输入匹配的一组字面量字符串、模式或一组标准。确认机制接受任何与白名单匹配的数据,并阻止其他数据。例如,在数据库中查询所需的产品代码时,应用程序可能会确认其仅包含字母数字字符,长度正好为6个字符。根据随后对产品代码进行的处理,开发者知道通过这种测试的输入不会造成任何问题。

在切实可行的情况下,这种方法是处理潜在恶意输入的最有效方法。因为在制定白名单时已经非常小心,所以攻击者无法使用专门设计的输入来干扰应用程序的行为。然而,在许多情况下,应用程序必须接受并不满足任何已知“正常”标准的数据,并对其进行处理。例如,在一些人的姓名中包含撇号和连字符的情况。这些数据可用于对数据库发动攻击。但也可能存在这样的要求,即应用程序应允许任何人以真实姓名注册。因此,虽然这种方法极其有效,但基于白名单的方法并非是解决处理用户输入问题的万能办法。

净化

这种方法认可有时需要接受无法保证其安全的数据。应用程序并不拒绝这种输入,相反,它以各种方式对其进行净化,防止它造成任何不利的影响。数据中可能存在的恶意字符被彻底删除掉,只留下已知安全的字符,或者在进一步处理前对它们进行适当编码或“转义”。

基于数据净化的方法一般非常有效。在许多情况下,可将其作为处理恶意输入问题的通用解决办法。例如,在将危险字符植入应用程序页面前对其进行HTML编码,是防御跨站点脚本攻击的常用方法(请参阅第12章了解相关内容)。然而,如果需要在一个输入项中容纳几种可能的恶意数据,可能就很难对其进行有效的净化。这时,最好采用边界确认方法处理用户输入,如后文所述。

安全数据处理

以不安全的方式处理用户提交的数据,是许多Web应用程序漏洞形成的根本原因。通常,不需要确认输入本身,只需确保处理过程绝对安全,即可避免这些漏洞。有些时候,可使用安全的编程方法避免常见问题。例如,在数据库访问过程中正确使用参数化查询,就可以避免SQL注入攻击(请参阅第9章了解相关内容)。在其他情况下,完全可以避免应用程序功能设计不安全的做法,如向操作系统命令解释程序提交用户输入。

这种方法并不适用于Web应用程序需要执行的每项任务,但如果适用,它是一种有效处理潜在恶意输入的通用方法。

语法检查

迄今为止,本书描述的防御措施全都用于防止应用程序接受各种错误的输入,攻击者专门设计这些输入的内容以干扰应用程序的处理过程。然而,在一些漏洞中,攻击者提交的输入与普通的非恶意用户提交的输入完全相同。之所以称其为恶意输入,是因为攻击者提交的动机不同。例如,攻击者可能会修改通过隐藏表单字段提交的账号,企图访问其他用户的银行账户。这时,再多的语法确认也无法区别用户与攻击者的数据。为防止未授权访问,应用程序必须确认所提交的账号属于之前提交该账号的用户。

边界确认

在信任边界确认数据的做法并不少见。用户提交的数据不可信是造成Web应用程序核心安全问题的主要原因。虽然在客户端执行的输入确认检查可以提高性能,改善用户体验,但它们并不能为实际到达服务器的数据提供任何保证。服务器端应用程序第一次收到用户数据的地方是一个重要的信任边界,应用程序需要在此采取措施防御恶意输入。

鉴于核心问题的本质,可以基于因特网(“不良”且不可信)与服务器端应用程序(“正常”且可信)之间的边界来考虑输入确认问题。从这个角度看,输入确认的任务就是净化到达的潜在恶意数据,然后将“洁净的”数据提交给可信的应用程序。此后,数据即属于可信数据,不需要任何进一步的检查或担心可能的攻击,即可进行处理。

很明显,当我们开始分析一些实际的漏洞时,执行这种简单的输入确认是不够的,原因如下。

基于应用程序所执行功能的广泛性以及其所采用技术的多样性,一个典型的应用程序需要防御大量各种各样的基于输入的攻击,且每种攻击可能采用一组截然不同的专门设计的数据。因此,很难在外部边界建立一个单独的机制,防御所有这些攻击。

许多应用程序功能都涉及组合一系列不同类型的处理过程。用户提交的一项输入可能会在不同的组件中引发许多操作,其中前一个操作的输出结果被用于后一个操作的输入。

数据发生转换后,可能会变得与原始的输入完全不同。而经验丰富的攻击者能够操纵应用程序,在关键处理阶段生成恶意输入,攻击接收这些数据的组件。为此,很难在外部边界执行确认机制,预测每一个用户输入的全部可能处理结果。

防御不同类型的基于输入的攻击可能需要对相互矛盾的用户输入执行各种确认检查。例如,防止跨站点脚本攻击可能需要将 > 字符HTML编码为<,而防止命令注入攻击则需要阻止包含&与;字符的输入。有时候,想要在应用程序的外部边界同时阻止所有类型的攻击几乎是不可能的事情。

边界确认(boundary validation)是一种更加有效的模型。此时,服务器端应用程序的每一个单独的组件或功能单元将其输入当做来自潜在恶意来源的输入对待。除客户端与服务器之间的外部边界外,应用程序在上述每一个信任边界上执行数据确认。这种模型为前面提出的问题提供了一个解决方案。每个组件都可以防御它收到的特殊类型的专门设计的输入。当数据通过不同的组件时,即可对前面转换过程中生成的任意数据值执行确认检查。而且,由于在不同的处理阶段执行不同的确认检查,它们之间不可能发生冲突。

图2-5说明了一种典型情况,此时边界确认是防御恶意输入的最有效方法。在用户登录过程中,需要对用户提交的输入进行几个步骤的处理,并在每个步骤执行适当的确认检查。

图2-5 一种在多阶段处理步骤中使用边界确认的应用程序功能

(1)应用程序收到用户的登录信息。表单处理程序确认每个输入仅包含合法字符,符合特殊的长度限制,并且不包含任何已知的攻击签名。

(2)应用程序执行一个SQL查询检验用户证书。为防止SQL注入攻击,在执行查询前,应用程序应对用户输入中包含的可用于攻击数据库的所有字符进行转义。

(3)如果用户成功登录,应用程序再将用户资料中的某些数据传送给SOAP服务,进一步获得用户账户的有关信息。为防止SOAP注入攻击,需要对用户资料中的任何XML元字符进行适当编码。

(4)应用程序在用户的浏览器中显示用户的账户信息。为防止跨站点脚本攻击,应用程序对植入返回页面的任何用户提交的数据执行HTML编码。

我们将在后续章节详细介绍上文描述的特殊漏洞和防御机制。如果这一功能发生变化,需要向其他应用程序组件提交数据,那么可能需要在相关信任边界执行类似的防御。例如,如果登录失败致使应用程序向用户发送警告电子邮件,那么可能需要检查合并到电子邮件中的所有用户数据,防止SMTP注入攻击。

多步确认与规范化

在确认检查过程中,当需要在几个步骤中处理用户提交的输入时,就会出现一个输入处理机制经常遇到的问题。如果不谨慎处理这个过程,那么攻击者就能够建立专门设计的输入,使恶意数据成功避开确认机制。当应用程序试图通过删除或编码某些字符或表达式净化用户输入时,就会出现这种问题。例如,为防御某些跨站点脚本攻击,应用程序可能会从任何用户提交的数据中删除表达式:

但攻击者可通过应用以下输入避开过滤器:

由于过滤无法递归运行,删除被阻止的表达式后,表达式周围的数据又合并在一起,重新建立恶意表达式。

同样,如果对用户输入执行几个确认步骤,攻击者就可以利用这些步骤的顺序来避开过滤。例如,如果应用程序首先递归删除..\,然后递归删除..\,就可以使用以下输入避开确认检查:

数据规范化(data canonicalization)会造成另一个问题。当用户浏览器送出输入时,它可对这些输入进行各种形式的编码。之所以使用这些编码方案,是为了能够通过HTTP安全传送不常见的字符与二进制数据(请参阅第3章了解更多详情)。规范化是指将数据转换或解码成一个常见字符集的过程。如果在实施输入过滤之后才执行规范化,那么攻击者就可以通过使用编码避开确认机制。

例如,应用程序可能会从用户输入中删除省略号,以防止某些SQL注入攻击。但是,如果应用程序随后对净化后的数据进行规范化,那么攻击者就可以使用URL编码的输入避开确认:

收到该输入后,应用程序服务器会执行正常的URL解码,因此该输入变为:

其中并不包含省略号,因此,应用程序的过滤器允许该输入。但是,如果应用程序执行进一步的URL解码,该输入将变为省略号,从而避开过滤。

如果应用程序删除而不是阻止省略号,然后执行进一步的规范化,则可以使用以下输入避开过滤:

值得注意的是,在这些情况下,应用程序服务器端不一定会执行多步确认和规范化。例如,在下面的输入中,几个字符已被HTML编码:

如果服务器端应用程序使用输入过滤来阻止某些JavaScript表达式和字符,该已编码的输入就可以成功避开过滤。但是,如果该输入随后被复制到应用程序的响应中,某些浏览器将对src参数值执行HTML解码,嵌入的JavaScript将得以执行。

除了供Web应用程序使用的标准编码方案外,其他情况下,如果应用程序采用的组件将数据从一个字符集转换为另一个字符集,这也会导致规范化问题。例如,某些技术会基于印刷字形的相似性,对字符执行“最佳”映射。这时,字符《和》分别被转换为<和>,Ÿ和则被转换为Y和A。攻击者经常利用这种方法传送受阻止的字符或关键字,从而避开应用程序的输入过滤。

本书将详细介绍这类攻击,它们可有效挫败应用程序针对常见的基于输入的漏洞而采取的许多防御机制。

有时候,可能很难避免多步确认与规范化造成的问题,也不存在解决这类问题的唯一方案。一种解决办法是递归执行净化操作,直到无法进一步修改输入。然而,如果需要在净化过程中对一个存在疑问的字符进行转义,那么这种情况可能会造成无限循环。通常,这个问题只有根据具体情况、基于所执行的确认类型加以解决。如果可能,最好避免净化某些不良输入的做法,完全拒绝这种类型的输入。

处理攻击者

任何设计安全应用程序的开发人员必须基于这样一个假设:应用程序将成为蓄意破坏且经验丰富的攻击者的直接攻击目标。能够以受控的方式处理并应对这些攻击,是应用程序安全机制的一项主要功能。这些机制通常结合使用一系列防御与攻击措施,以尽可能地阻止攻击者,并就所发生的事件,通知应用程序所有者以及提供相应的证据。为处理攻击者而采取的措施一般由以下任务组成:

处理错误;

维护审计日志;

向管理员发出警报;

应对攻击。

处理错误

不管应用程序开发者在确认用户输入时多么小心,还是几乎可以肯定会出现一些无法预料的错误。功能与用户验收测试过程能够查明普通用户行为造成的错误,在生产环境中部署应用程序前应当考虑到这一因素。然而,我们无法预测恶意用户与应用程序交互的每一种可能方式,并且当应用程序遭受攻击时肯定会出现其他错误。

应用程序的一个关键防御机制是合理地处理无法预料的错误,要么纠正这些错误,要么向用户发送适当的错误消息。在生产环境下,应用程序不应在其响应中返回任何系统生成的消息或其他调试信息。过于详细的错误消息非常有利于恶意用户向应用程序发动进一步攻击。有些情况下,攻击者能够利用存在缺陷的错误处理方法从错误消息中获得敏感信息;此时,错误消息成为攻击者从应用程序中窃取数据的重要渠道。图2-6显示了一个由无法处理的错误生成的过于详细的错误消息。

图2-6 一个无法处理的错误

大多数Web开发语言通过try-catch块和受查异常提供良好的错误处理支持。应用程序代码应广泛使用这些方法查明特殊与常规错误,并做出相应处理。而且,还可以配置大多数应用程序服务器,使其以自定义的方式处理无法处理的应用程序错误,如提供不包含太多信息的错误消息。请参阅第15章了解有关这些措施的更多详情。

有效的错误处理措施通常与应用程序的日志机制整合在一起,后者尽可能地记录与无法预料的错误有关的调试信息。通常,无法预料的错误往往能够指明应用程序的防御机制中存在的缺陷。如果应用程序的所有者获得必要的信息,就能从源头解决这些问题。

维护审计日志

审计日志(audit log)在调查针对应用程序的入侵尝试时会发挥很大作用。发生入侵后,有效的审计日志功能应能够帮助应用程序所有者了解实际发生的情况,如哪些漏洞(如果有)被加以利用,攻击者是否可以对数据进行非法访问或执行任何未授权的操作,并尽可能地提供侵入者的身份信息。

在任何注重安全的应用程序中,日志应记录所有重要事件。一般这些事件应至少包括以下几项。

所有与身份验证功能有关的事件,如成功或失败的登录、密码修改。

关键交易,如信用卡支付与转账。

被访问控制机制阻止的访问企图。

任何包含已知攻击字符串,公然表明恶意意图的请求。

许多安全性至关重要的应用程序(如电子银行使用的应用程序)会完整记录客户端提出的每一个请求,这样可为任何事故调查提供全面的司法记录。

有效的审计日志功能一般会记录每个事件的发生时间、发出请求的IP地址和用户的账户(如果通过验证)。这些日志必须受到严格保护,避免未授权的读取或写入访问。一种有效的保护方法是将审计日志保存在仅接受主应用程序送出的更新消息的自治系统中。某些情况下,可能需要将日志复制到一次性写入的媒质中,确保它们的完整性,以便在遭受攻击后进行调查。

在受攻击面方面,保护不严密的审计日志可能为攻击者提供大量信息,向其披露许多敏感信息,如会话令牌和请求参数,这些信息可能会使攻击者能够立即攻破整个应用程序(见图2-7)。

图2-7 保护不严密、包含其他用户提交的敏感信息的应用程序日志

向管理员发出警报

审计日志可帮助应用程序所有者调查入侵企图,如有可能,应对侵入者采取法律行动。然而,许多时候我们希望立即采取行动,实时响应攻击企图。例如,管理员可能会阻止被攻击者利用的IP地址或用户账户。在极端情况下,他们甚至可能在调查攻击、采取补救措施时将应用程序从网络中断开。这时,即使攻击者已经成功侵入应用程序,如果能够及时采取防御措施,也可以将实际影响降到最低。

许多时候,警报机制必须在两个相互矛盾的目标之间取得平衡,既准确报告每次的真实攻击,又不会生成过多警报,造成它们被管理员忽略。精心设计的报警机制能够组合各种因素,确定应用程序正在遭受的某种攻击;并在可能的情况下将所有相关事件集中到一个警报中。警报监控的反常事件一般包括以下几种。

应用反常,如收到由单独一个IP地址或用户发出的大量请求,表明应用程序正受到自定义攻击。

交易反常,如单独一个银行账户所转入或转出的资金数量出现异常。

包含已知攻击字符串的请求。

请求中普通用户无法查看的数据被修改。

现有的应用程序防火墙和入侵检测产品能够相当完善地提供其中一些功能。这些产品一般组合应用一组基于签名与异常的规则来确定对应用程序的恶意利用,并能够主动阻止恶意请求,向管理员发出警报。这些产品构成保护Web应用程序的一个重要防御层,当已知现有应用程序存在漏洞,但可用资源却无法修复这些漏洞时,它们特别有用。然而,由于每个Web应用程序都各不相同,这些产品的效用也往往受到限制,其采用的规则也因而趋于一般化。Web应用程序防火墙通常能够确定最明显的攻击,在这种攻击中,攻击者在每一个请求参数中提交标准的攻击字符串。然而,与这种攻击相比,许多攻击往往更加隐蔽,例如修改隐藏表单字段中的账号来访问其他用户的数据,或者提交无序请求以利用应用程序逻辑中存在的缺陷。在这些情况下,攻击者提交的请求可能与善意用户提交的请求完全相同。之所以称为恶意请求,是因为提交请求的环境有所不同。

在任何安全性至关重要的应用程序中,进行实时警报的最有效方法是将其与应用程序的输入确认机制和其他控制方法紧密结合起来。例如,如果认为cookie中包含一组特殊值中的某个值,那么任何违反这种情况的现象即表明该值已被修改,而且应用程序的普通用户无法执行此类修改。同样,如果一名用户修改隐藏表单字段中的账号,以确定另一名用户的账户,这种做法也明确表现出恶意意图。应用程序的主要防御机制应阻止这些攻击,而且,这些保护机制可轻易与应用程序的警报机制进行整合,提供完全自定义的恶意行为警示。因为已经根据应用程序的实际逻辑定制这些检查,如果清楚了解普通用户的操作权限,那么不管任何现有的解决方案多么易于配置,与之相比,它们都能提供更加准确的警报。

应对攻击

除向管理员发出警报外,许多安全性至关重要的应用程序还含有内置机制,以防御潜在恶意用户。

由于应用程序各不相同,现实世界中的许多攻击要求攻击者系统地探查应用程序中存在的漏洞,提交无数包含专门设计的输入请求,以确定其中是否存在各种常见的漏洞。高效的输入确认机制能够把许多这种类型的请求确定为潜在的恶意请求,并阻止这些输入,防止它们给应用程序造成任何不利影响。然而,我们还应意识到,攻击者仍然能够以某种方式避开这些过滤;而且,应用程序确实包含某些实际的漏洞,等待攻击者去发现和利用。从某种意义上说,进行系统性探查的攻击者可能会发现这些缺陷。

有鉴于此,一些应用程序采取自动反应措施阻止攻击者进行这种形式的探查,例如对攻击者提交的请求的响应速度变得越来越慢,或者终止攻击者的会话,要求其重新登录或在继续攻击前执行其他步骤。虽然这些措施无法阻挡最有耐心和决心的攻击者,但能够阻止许多很随意的攻击者,并且为管理员监控此类情况、在必要时采取更加严厉的措施赢得时间。

当然,阻止显而易见的攻击并不如修复应用程序中存在的所有漏洞重要。然而,在现实情况中,即使我们为清除应用程序中的安全缺陷做出了不懈努力,仍然会有一些可供利用的缺陷存在。给攻击者设置更多阻碍是一种有效的深层防御措施,这样做能够降低任何残存的漏洞被发现和利用的可能性。

管理应用程序

任何有用的应用程序都需要进行管理与维护,这种功能通常是应用程序安全机制的一个重要组成部分,可帮助管理员管理用户账户与角色、应用监控与审计功能、执行诊断任务并配置应用程序的各种功能。

许多应用程序一般通过相同的Web界面在内部执行管理功能,这也是它的核心非安全功能,如图2-8所示。在这种情况下,管理机制就成为应用程序的主要受攻击面。它吸引攻击者的地方主要在于它能够提升权限,以下举例说明。

图2-8 Web应用程序中的管理界面

身份验证机制中存在的薄弱环节使攻击者能够获得管理员权限,迅速攻破整个应用程序。

许多应用程序并不对它的一些管理功能执行有效的访问控制。利用这个漏洞,攻击者可以建立一个拥有强大特权的新用户账户。

管理功能通常能够显示普通用户提交的数据。管理界面中存在的任何跨站点脚本缺陷都可能危及用户会话的安全。

因为管理用户被视为可信用户,或者由于渗透测试员只能访问低权限的账户,所以管理功能往往没有经过严格的安全测试。而且,它通常需要执行相当危险的操作,包括访问磁盘上的文件或操作系统命令。如果一名攻击者能够攻破管理功能,就能利用它控制整个服务器。

小结

尽管存在巨大差异,但几乎所有的Web应用程序都以某种形式采用相同的核心安全机制。这些机制是应用程序应对恶意用户所采取的主要防御措施,因而应用程序的受攻击面大部分也由它们构成。我们在本书后面介绍的漏洞也主要源于这些核心机制中存在的缺陷。

在这些机制中,处理用户访问和用户输入的机制是最重要的机制。当针对应用程序发动攻击时,它们将成为主要攻击对象。利用这些机制中存在的缺陷通常可以完全攻破整个应用程序,使攻击者能够访问其他用户的数据、执行未授权操作以及注入任意代码和命令。

问题

欲知问题答案,请访问http://mdsec.net/wahh[2]。

(1)为什么说应用程序处理用户访问的机制是所有机制中最薄弱的机制?

(2)会话与会话令牌有何不同?

(3)为何不可能始终使用基于白名单的方法进行输入确认?

(4)攻击者正在攻击一个执行管理功能的应用程序,并且不具有使用这项功能的任何有效证书。为何他仍然应当密切关注这项功能呢?

(5)旨在阻止跨站点脚本攻击的输入确认机制按以下顺序处理一个输入:

(a)删除任何出现的<script>表达式;

(b)将输入截短为50个字符;

(c)删除输入中的引号;

(d)对输入进行URL解码;

(e)如果任何输入项被删除,返回步骤(1)。

是否能够避开上述确认机制,让以下数据通过确认?

注释

[1]在软件环境中,受攻击面(attack surface)是指对未通过验证的用户的有效功能;也就是说,未通过验证的用户通过软件的默认配置能够达到什么目的。——译者注

[2]答案的中文版请参阅图灵社区本书页面(http://www.ituring.com.cn/book/885),或http://blog.sina.com.cn/s/blog_545eb7860101379s.html。——译者注

浙ICP备11005866号-12