攻击会话管理

在绝大多数Web应用程序中,会话管理机制是一个基本的安全组件。它帮助应用程序从大量不同的请求中确认特定的用户,并处理它收集的关于用户与应用程序交互状态的数据。会话管理在应用程序执行登录功能时显得特别重要,因为它可在用户通过请求提交他们的证书后,持续向应用程序保证任何特定用户身份的真实性。

由于会话管理机制所发挥的关键作用,它们成为针对应用程序的恶意攻击的主要目标。如果攻击者能够破坏应用程序的会话管理,他就能轻易避开其实施的验证机制,不需要用户证书即可伪装成其他应用程序用户。如果攻击者以这种方式攻破一个管理用户,那么他就能够控制整个应用程序。

和验证机制一样,通常会话管理功能中也存在着大量缺陷。在最容易遭受攻击的情况下,攻击者只需递增应用程序向他们发布的令牌值,就可以转换到另一名用户的账户。在这种情况下,任何人都可以访问应用程序的全部功能。另一方面,如果应用程序受到严密保护,攻击者必须付出巨大的努力,破解几层模糊处理并实施复杂的自动攻击,才能发现应用程序中存在的细小漏洞。

本章将分析我们在现实世界的Web应用程序中发现的各种漏洞,详细说明发现和利用这些漏洞所需执行的实际步骤。最后还将描述应用程序为防止这些攻击所应采取的防御措施。

 错误观点 “我们使用智能卡进行验证,没有智能卡攻击者不可能攻破用户会话。”

无论应用程序的验证机制多么安全稳定,只有通过会话用户随后提出的请求才能与验证机制建立联系。如果应用程序的会话管理存在缺陷,攻击者仍然能够避开可靠的验证机制,危及用户的安全。

状态要求

从本质上讲,HTTP协议没有状态。它基于一种简单的请求—响应模型,其中每对消息代表一个独立的事务。协议本身并无将某位用户提出的各种请求联系起来的机制,并将它们与Web服务器收到的所有其他请求区分开来。在Web发展的早期阶段,并没有必要建立这种机制:因为Web站点公布的是任何人都可以查阅的静态HTML页面。但如今,情况已经发生了巨大变化。

绝大多数的Web“站点”实际为Web应用程序。它们允许用户注册与登录;帮助用户购买及销售产品。它们能够在用户下次访问时记住他的喜好。它们可根据用户的单击和输入,通过动态建立的内容提供丰富、多媒体形式的使用体验。为执行这些功能,应用程序就需要使用会话。

支持登录是会话在应用程序中最主要的用途。输入用户名和密码后,可以用输入的证书所属的用户身份使用应用程序,直到退出会话或由于会话处于非活动状态而终止。用户不希望在每个应用程序页面重复输入密码。因此,一旦用户通过验证,应用程序就会为他建立一个会话,把所有属于这个会话的请求当做该用户提出的请求处理。

不具备登录功能的应用程序通常也需要使用会话。许多出售商品的站点并不要求顾客建立账户。但是,它们允许用户浏览目录、往购物篮中添加商品、提供交货信息并进行支付。在这种情形下,就没有必要验证用户的身份:应用程序并不知道或关心绝大多数用户的身份。但是,为了与他们进行交易,应用程序需要知道它收到的哪些请求来自同一名用户。

执行会话最简单、最常见的方法就是向每名用户发布一个唯一的会话令牌或标识符。用户在随后向应用程序提出的每一个请求中提交这个令牌,帮助应用程序在当前请求与前面提出的请求之间建立关联。

在大多数情况下,应用程序使用HTTP cookie作为在服务器与客户端间传送这些会话令牌的传输机制。服务器对新客户端的第一个响应中包含以下HTTP消息头:

客户端随后提出的请求中包含如下消息头:

这种标准的会话管理机制非常容易受到各种类型的攻击。当攻击会话机制时,攻击者的主要目标是以某种方式劫持一名合法用户的会话,由此伪装成这名用户。如果该用户已经通过应用程序的验证,攻击者就可以访问属于这名用户的私有数据,或者以他的身份执行未授权操作。如果该用户未能通过验证,攻击者仍然能够查看用户在会话过程中提交的敏感信息。

和前面示例中运行ASP.NET的Microsoft IIS服务器一样,许多商业Web服务器和Web应用程序平台执行它们自己的基于HTTP cookie的非定制会话管理解决方案。Web应用程序开发者可使用它们提供的API将会话依赖功能与这种解决方案整合起来。

事实证明,一些非定制会话管理解决方案易于受到各种攻击,导致用户的会话被攻破(这一问题将在本章后面讨论)。此外,一些开发者发现,他们需要比内置解决方案所提供的控制更加全面的会话行为控制,或者希望避免基于cookie的解决方案中存在的一些固有漏洞。鉴于这些原因,安全性至关重要的应用程序(如电子银行)通常使用定制或并非基于cookie的会话管理机制。

会话管理机制中存在的漏洞主要分为两类:

会话令牌生成过程中的薄弱环节;

在整个生命周期过程中处理会话令牌的薄弱环节。

我们将分别分析这些弱点,描述在现实世界的会话管理机制中常见的各种漏洞,以及发现和利用这些漏洞的实用技巧。最后将描述应用程序为防止这些攻击所应采取的防御措施。

渗透测试步骤

许多应用程序使用标准的cookie机制传输会话令牌,这样可直接确定哪些数据包含令牌。然而,在其他情况下,可能需要进行一番探测才能找到令牌。

(1)应用程序常常使用几个不同的数据共同表示一个令牌,包括cookie、URL参数和隐藏表单字段。其中一些数据可用于在不同的后端组件中维护会话状态。如果没有得到确认,不要想当然地认为某个特殊的参数就是会话令牌,或者只使用一个数据追踪会话。

(2)有时,一些数据似乎是应用程序的会话令牌,其实并非如此。具体来说,由Web服务器或应用程序平台生成的标准会话cookie可能存在,但实际并不被应用程序使用。

(3)用户通过验证后,观察浏览器收到哪些新数据项。应用程序通常会在用户通过验证后建立新的会话令牌。

(4)为确定应用程序到底使用哪些数据项作为令牌,找到一个确信依赖会话的页面(如某一名用户的“用户资料”页面),并向它提出几个请求,系统性地删除疑似被用作令牌的数据。如果删除某个数据后,应用程序不再返回会话依赖页面,即可确定该数据可能为会话令牌。Burp Repeater是执行这类测试的有效工具。

会话替代方案

并非每一种Web应用程序都使用会话,一些具备验证机制、功能复杂的安全性至关重要的应用程序选择使用其他技术管理状态。常见的会话替代方案有两种。

HTTP验证。使用各种基于HTTP验证技术(基本、摘要、NTLM验证等)的应用程序有时避免使用会话。在HTTP验证中,客户端组件使用HTTP消息头通过浏览器直接与验证机制交互,而不是通过包含在任何单独页面中的针对特定应用程序的代码与验证机制交互。一旦用户在浏览器对话框中输入他的证书,浏览器将会在随后向同一服务器提出的每个请求中重复提交这些证书(或重复执行任何必要的握手)。这种做法等同于应用程序使用基于HTML表单的验证,并在每个应用程序页面插入一个登录表单,要求用户通过他们执行的每一项操作重复验证自己的身份。因此,如果使用基于HTTP的验证,应用程序可以不必使用会话,而通过多个请求重复确定用户身份。然而,基于因特网的应用程序很少使用HTTP验证。而且,由于会话机制发展完善,能够提供其他用途非常广泛的功能,实际上,几乎所有的Web应用程序都采用这种机制。

无会话状态机制。一些应用程序并不发布会话令牌管理用户与应用程序的交互状态,而是传送所有必要数据(一般保存在cookie或隐藏表单字段中),由客户端管理状态。实际上,这种机制以类似于ASP.NET ViewState的方式使用无会话状态。为保证这种机制的安全,必须对通过客户端传送的数据加以适当保护。这通常要求建立一个包含所有状态信息的二进制巨对象,并使用一种公认的算法对这些数据进行加密或签名。还必须在数据中包含足够的上下文,以防止攻击者将在应用程序某个位置收集到的状态对象提交到另一个位置,造成某种意外行为。应用程序还必须在对象的数据中包含一个终止时间,执行与会话超时相同的功能。我们已在第5章详细介绍过通过客户端传送数据的各种安全机制。

渗透测试步骤

(1)如果应用程序使用HTTP验证,它可能并不执行会话管理机制。使用前面描述的方法分析任何可能是令牌的数据的作用。

(2)如果应用程序使用无会话状态机制,通过客户端传送所有必要数据进行状态维护,有时我们可能很难检测出这种机制,但如果发现下列迹象,即可确定应用程序使用这种机制。

向客户端发布的可能令牌的数据相当长(如100 B或超过100 B)。

应用程序对每个请求做出响应,发布一个新的类似令牌的数据。

数据似乎被加密(因此无法辨别其结构)或包含签名(由有意义的结构和几个字节的无意义二进制数据组成)。

应用程序拒绝通过多个请求提交相同数据的做法。

(3)如果相关证据明确表明应用程序并未使用会话令牌管理状态,那么本章描述的任何攻击都不可能达到其目的。因此,最好着手去寻找其他严重的漏洞,如访问控制不完善或代码注入。

会话令牌生成过程中的薄弱环节

由于生成令牌的过程不安全,攻击者能够确定发布给其他用户的令牌,致使会话管理机制易于受到攻击。

 注解 许多时候,应用程序的安全取决于它所生成的令牌的不可预测性,以下是一些示例:

发送到用户注册的电子邮件地址的密码恢复令牌;

隐藏表单字段中用于防止跨站点请求伪造攻击(请参阅第13章)的令牌;

用于一次性访问受保护的资源的令牌;

“记住我”功能使用的永久令牌;

未使用验证的购物应用程序的消费者用于检索现有订单的当前状态的令牌。

在本章中,我们主要讨论适用于上述所有情形的与令牌生成有关的缺陷。实际上,由于当前的许多应用程序都采用成熟的平台机制来生成会话令牌,因此,往往会在这些功能区域发现有关令牌生成的可利用缺陷。

令牌有一定含义

一些会话令牌通过用户的用户名或电子邮件地址转换而来,或者使用与其相关的其他信息创建。这些信息可以某种方式进行编码或模糊处理,也可与其他数据结合在一起。

例如,初看起来,下面的令牌由一长串随机字符组成:

但是,仔细分析后发现,其中仅包含十六进制字符。猜想这个字符串可能是一个经过十六进制编码的ASCII字符串,我们使用解码器对其解码,发现它实际是:

攻击者可以利用这个会话令牌的含义猜测其他应用程序用户的当前会话。使用一组枚举出的用户名或常见用户名,就能够迅速生成大量可能有效的令牌,并进行测试以确定它们是否有效。

包含有含义数据的令牌通常表现出某种结构。也就是说,它们由几种成分组成,通常以分隔符隔开,攻击者可分别提取并分析这些成分,以了解它们的功能和生成方法。结构化令牌的组成成分包括以下几项。

账户用户名。

应用程序用来区分账户的数字标识符。

用户姓名中的名/姓。

用户的电子邮件地址。

用户在应用程序中所属的组或扮演的角色。

日期/时间戳。

一个递增或可预测的数字。

客户端的IP地址。

为特意对其内容进行模糊处理,或者只是为了确保二进制数据能通过HTTP安全传输,应用程序会对结构化令牌中的每个不同成分或整个令牌以不同方式进行编码。常用的编码方案包括XOR、Base64和使用ASCII字符的十六进制表示法(请参阅第3章了解相关内容)。为将其恢复到原始状态,可能有必要对结构化令牌的每一个成分使用各种不同的解码方法。

 注解 当处理包含结构化令牌的请求时,应用程序可能不会处理令牌中的每一个成分或每个成分中的所有数据。在前面的示例中,应用程序可能会对令牌进行Base64解码,然后只处理其中的“用户”(user)和“日期”(date)成分。如果令牌中包含一个二进制巨对象,那么这些数据中的大部分为填充数据,只有一小部分数据与服务器在令牌上执行的确认有关。减少令牌中确实必需的成分的数量通常可显著降低令牌的复杂程度。

渗透测试步骤

(1)从应用程序中获取一个令牌,对其进行系统化的修改,以确定整个令牌是否有效,或者令牌的某些成分是否被忽略。尝试以一次一个字节(或者一次一个位)的方式更改令牌的值,然后重新向应用程序提交修改后的令牌,看应用程序是否仍然接受这个令牌。如果发现令牌中的某些部分实际上并无作用,可以将它们排除在深入分析之外,以减轻工作负担。可以使用Burp Intruder中的“char frobber”有效载荷类型修改令牌的值,每次修改一个字符,以帮助完成此任务。

(2)在不同时间以不同的用户登录,记录服务器发布的令牌。如果应用程序允许自我注册,可以选择自己的用户名,用一系列存在细微差别的相似用户名登录,如A、AA、AAA、AAAA、AAAB、AAAC、AABA等。如果其他与某一名用户有关的数据(如电子邮件地址)在登录阶段提交或保存在用户资料中,对其进行与前面类似的系统化修改,并记录登录后收到的令牌。

(3)对令牌进行分析,查找任何与用户名和其他用户可控制的数据有关的内容。

(4)分析令牌,查找任何明显的编码或模糊处理方案。如果用户名包含一组相同的字符,在令牌中寻找可能使用XOR模糊处理的对应字符序列;在令牌中寻找仅包含十六进制字符的字符序列,它表示应用程序可能对ASCII字符串进行了十六进制编码处理,或者披露其他信息。寻找以等号(=)结尾的字符序列或仅包含其他有效Base64字符的序列,如a~z、A~Z、0~9、+和/。

(5)如果对会话令牌样本进行逆向工程可获得任何有意义的结果,看看是否拥有足够的信息可猜测出应用程序最近向其他用户发布的令牌。找到一个依赖会话的应用程序页面(即如果不使用有效会话访问,就会返回错误消息或指向其他位置的重定向页面),通过Burp Intruder之类的工具可使猜测出的令牌向该页面提出大量请求。监控页面被正确加载的所有情况的结果,这表示会话令牌有效。

尝试访问

http://mdsec.net/auth/321/

http://mdsec.net/auth/329/

http://mdsec.net/auth/331/

令牌可预测

一些会话令牌并不包含与某个特定用户有关的任何有意义的数据,但由于它们包含某种顺序或模式,允许攻击者通过几个令牌样本即可推断出应用程序最近发布的其他有效令牌,因此具有可预测性。即使推断过程需要做出大量尝试,并且成功率极低(例如,每1000次尝试得到一个有效令牌),自动攻击工具也仍然能够利用这种缺陷在很短的时间内确定大量有效令牌。

与定制应用程序相比,会话管理的商业应用(如Web服务器或Web应用程序平台)中的令牌可预测漏洞更容易被发现。当向一个定制会话管理机制实施远程攻击时,攻击者所能获得的已发布令牌样本的数量可能受到服务器容量、其他用户的活动、带宽、网络延时等因素的限制。然而,在实验室环境中,渗透测试员可以迅速建立数百万个令牌样本,所有样本都紧密相连,并使用了时间戳,而且可以降低其他用户造成的干扰。

在最简单也是最容易受到攻击的情况下,应用程序使用一个简单的连续数字作为会话令牌。这时,攻击者只需获得两个或三个令牌样本就可以实施攻击,并立即截获当前有效的所有令牌。

图7-1表示正在使用Burp Intruder循环访问一个连续会话令牌的最后两位数字,以查找会话仍处于活动状态可被劫持的令牌值。这时,服务器响应的长度是发现有效会话的可靠指标。并且从中提取的grep特性也可用于显示每个会话登录用户的用户名。

图7-1 会话令牌可预测时查找有效会话的一次攻击

在其他情况下,应用程序令牌中可能包含更加复杂的序列,需要付出一定的努力才能发现。序列的变化形式可能多种多样,但是,根据我们的行业经验,可预测的会话令牌通常来自3个方面:

隐含序列;

时间依赖;

生成的数字随机性不强。

我们将分别讨论这3种情况。

隐含序列

有时,对会话令牌的原始形式进行分析可能无法预测它们;但是,对其进行适当解码或解译就可以揭示其中包含的序列。

以下面一组值为例,它是一个结构化会话令牌的组成成分:

一眼看来,其中并不包含任何模式。然而,粗略检查后发现,令牌中可能包含Base64编码的数据,除大小写混合的字母和数字字符外,其中还有一个+字符,它也是一个有效的Base64字符串。使用Base64解码器对令牌解码,得到以下结果:

很明显,这些字符串是乱码,并且其中包含非打印字符。通常来说,这表示处理的是二进制数据,而非ASCII文本。将解码后的数据以十六进制表示,得到:

其中仍然没有明显的模式。然而,如果用前一个数字减去后一个数字,就会得到以下结果:

隐含的模式立即显露出来。生成令牌的算法如下:用前一个值加上0x97C4EB6A,把结果截短成一个32位的数字,并对这个二进制数据进行Base64编码,使其能够通过基于文本的HTTP协议传输。了解到这一点,就能轻易编写出一段脚本,生成服务器接下来产生的令牌,以及它在被截获的样本之前产生的令牌。

时间依赖

一些Web服务器和应用程序使用时间作为令牌值的输入,通过某种算法生成会话令牌。如果没有在算法中合并足够的熵[1],攻击者就可能推测出其他用户的令牌。虽然任何特定的令牌序列本身是完全随机的,但是,如果组合生成每个令牌的时间信息,也许可以发现某种可以辨别的模式。一个忙碌的应用程序每秒会生成大量的会话,因此实施一次自定义攻击就可以成功确定其他用户的大量令牌。

测试一家网上零售商的Web应用程序时,我们遇到以下令牌序列:

很明显,每个令牌由两个独立的数字组成:前一个数字的递增模式非常简单,很容易推测。后一个数字的递增值每次都有所变化。计算出每个连续令牌的递增值,得到以下结果:

这个序列并不包含可预测的模式。但显而易见的是,攻击者可以通过自动攻击确定相关的数字范围,通过蛮力攻击发现序列中的有效值。不过,在开始攻击前,等待几分钟后可截取另一个令牌序列:

将这个令牌序列与第一个序列进行比较,立即得到两个明显的结论。

第一个数字序列继续递增,但是,第一个数字序列后面遗漏了5个值。这可能是因为应用程序向其他用户发布了这5个值,他们在两次测试的间隙登录了应用程序。

第二个数字序列继续增大,其递增形式与第一个序列类似。但是,得到的第一个值比前一个值大539 578,大了许多。

第二次观测的结果立即让人产生警惕,推测时间在会话令牌生成过程中发挥的作用。显然,在两次截取令牌的过程中,应用程序只发布了5个令牌。然而,时间已经过去将近10分钟。最可能的解释是:令牌以秒为时间依赖,并可能以毫秒计算。

确实,预感是正确的。在随后的测试过程中进行一次代码审查,可发现以下令牌生成算法:

考虑对如何生成令牌所做的分析,攻击者可以直接构造一个自定义攻击,获得应用程序向其他用户发布的会话令牌。

继续从服务器中提取紧密相连的新会话令牌。

监控第一个数字的递增情况。如果递增值大于1,可知道应用程序向其他用户发布了一个令牌。

向其他用户发布一个令牌时,可立即知道发布时间的秒上限和下限,因为攻击者拥有在这个令牌之前和之后发布的令牌。由于不断获得新会话令牌,这些界限之间通常只包含几百个值。

应用程序每次向其他用户发布一个令牌,攻击者就实施一次蛮力攻击遍历这个范围内的每个数字,并把这个数字附加到已经发布给其他用户的第一个数字序列后面。攻击者尝试使用建立的每个令牌访问一个受保护的页面,直到尝试取得成功,攻破该用户的会话。

继续运行这个自定义攻击即可截获其他所有应用程序用户的令牌。一名管理用户登录后,整个应用程序将被完全攻破。

尝试访问

http://mdsec.net/auth/339/

http://mdsec.net/auth/340/

http://mdsec.net/auth/347/

http://mdsec.net/auth/351/

生成的数字随机性不强

计算机中的数据极少完全随机。因此,如果由于某种原因需要随机数据,一般通过软件使用各种技巧生成伪随机数字。所使用的一些算法生成看似随机并且在可能的数值范围内平均分布的序列,但有些人只需要少数几个样本,仍然能够准确推导出整个序列。

如果使用一个可预测的伪随机数字发生器生成会话令牌,那么得到的令牌就易于受到攻击者的攻击。

Jetty是一种完全以Java编写的常用Web服务器,它为在其上运行的应用程序提供一种会话管理机制。2006年,NGSSoftware的Chris Anley发现这种机制易于受到会话令牌预测攻击。该服务器使用Java API java.util.Random生成会话令牌。它执行一个“线性同余发生器”(linear congruential generator),通过以下算法生成序列中的下一个数字:

这种算法实际上是用生成的最后一个数字乘以一个常数,再加上另一个常数,生成下一个数字。得到的数字被截短至48位;然后,算法再将结果进行转换,返回请求方要求的位数。

了解了这种算法和由它生成的一个数字后,就可以轻易推算出接下来将要生成的数字,并且(利用一点数论知识)推导出它之前生成的数字。这意味着攻击者只需从服务器获得一个会话令牌,就可推测出所有当前和将来的会话令牌。

 注解 有时,令牌根据一个伪随机数字发生器的输出而生成,因此开发者决定将发生器的几个连续输出连接起来建立每个令牌。开发者认为使用这种方法可建立一个更长因而“更强大”的令牌。但是,这种策略通常是一种误解。如果攻击者能够获得发生器生成的几个连续输出,他们就可以通过它们推断出发生器内部状态的一些信息,因此更容易向前或向后推导发生器的输出顺序。

其他非定制应用程序框架在生成会话令牌时使用极其简单或可预测的熵源,其中许多熵源甚至可以确定。例如,PHP框架5.3.2及早期版本基于客户端的IP地址、生成令牌时的纪元时间、生成令牌时的微秒,以及线性同余发生器来生成会话令牌。虽然其中有几个未知值,但是,一些应用程序可以披露相关信息,从而推断出这些值。社交网络站点可能会记录用户的登录时间和IP地址。此外,该发生器使用的种子是PHP进程启动的时间,如果攻击者对服务器进行监视,就可以将这个值缩定在一个很小的范围内。

 注解 这是一个不断发展的研究领域。2001年,Full Disclosure邮件列表指出PHP会话令牌生成过程存在缺陷,但并未证实该缺陷可被利用。2010年,Samy Kamkar最终使用phpwn工具实现了对这一缺陷的利用。

测试随机性强度

某些时候,仅仅通过观察,或者通过适度的手动分析,就可以确定一系列令牌的模式。但是,通常而言,需要使用更加严格的方法来测试应用程序令牌的随机性强度。

完成测试的标准方法是应用统计假设测试原则,并采用各种严格的测试查找令牌样本的非随机性。测试过程的主要步骤如下。

(1)首先,假设令牌是随机生成的。

(2)进行一系列测试,通过每个测试观察可能具有某些特征的令牌样本(如果令牌是随机生成的)的特定属性。

(3)对于每个测试,假定以上假设是正确的,计算观察到的特征发生的机率。

(4)如果该几率在某一水平(显著性水平)之下,则否定上述假设,并得出结论——令牌不是随机生成的。

幸运的是,并不需要手动完成上述步骤!当前,Burp Sequencer是测试Web应用程序令牌随机性的最佳工具。该工具可灵活进行各种标准测试,并为你提供易于解释的明确结果。

要使用Burp Sequencer,需要从发布希望进行测试的令牌的应用程序中找到一个响应,如应用程序对发布包含会话令牌的新cookie的登录请求做出的响应。然后,从Burp的上下文菜单中选择“发送至sequencer”(send to sequencer),并在Sequencer配置中设置令牌在响应中的位置,如图7-2所示。还可以配置各种确定如何收集令牌的选项,然后单击“开始捕获”(start capture)按钮,开始收集令牌。如果已经通过其他方法(例如,通过保存某次Burp Intruder攻击的结果)获得适当的令牌样本,则可以使用“手动加载”(manual load)选项卡跳过令牌收集过程,直接进入统计分析阶段。

图7-2 将BurpSequencer配置为测试会话令牌的随机性

获得适当的令牌样本后,就可以对样本进行统计分析了。还可以在收集样本的同时进行中间分析。一般来说,获得更多样本可提高分析的可靠性。Burp需要的最小样本大小为100个令牌,但最好是收集更多样本。如果在分析几百个令牌后,结论表明令牌没有通过随机性测试,那么,可以确定,没有必要再收集其他令牌。否则,继续收集令牌并定期重新进行分析。如果收集了5000个令牌,并且结论表明这些令牌通过了随机性测试,则可以确定这一数量已经足够。但是,为符合正式的FIPS随机性测试,需要获得20 000个令牌样本,这是Burp支持的最大样本大小。

Burp Sequencer在字符和位级别执行统计测试。所有测试结果将进行汇总,以对令牌中的有效熵的位数(这是需要考虑的关键结果)进行总体评估。但是,还可以深入分析每项测试的结果,了解令牌的不同部分如何以及为何通过或未通过每项测试,如图7-3所示。用于每类测试的方法在测试结果下面进行了说明。

图7-3 分析BurpSequencer结果以了解所测试令牌的属性

需要注意的是,Burp会对令牌中的每个字符和数据位单独进行所有测试。许多时候,你会发现,大部分的结构化令牌都不是随机的,这本身并不表示存在任何类型的缺陷。重要的是,令牌应包含足以通过随机性测试的位数。例如,如果某个大令牌包含1000位信息,但其中只有50个位通过了随机性测试,那么,总体而言,该令牌还不如一个完全通过随机性测试的50位令牌可靠。

 注解 在进行随机性统计测试时,请记住两个要点。这些要点会影响你对测试结果的解释,并会影响应用程序的安全状态。首先,以完全确定的方式生成的令牌可能会通过随机性统计测试。例如,线性同余伪随机数字发生器,或计算连续数字的散列的算法,可能会生成通过测试的输出。但是,如果攻击者了解该算法和发生器的内部状态,就可以非常准确地正向或逆向推断出它的输出。

其次,未通过随机性统计测试的令牌实际上也许根本无法预测。如果令牌中的特定数据位没有通过测试,这只是说明在该位置观察到的数据位序列包含真正随机的令牌中不可能出现的特征。但是,如果尝试根据观察到的特征预测该数据位在下一个令牌中的值,这样的做法无异于盲目猜测。鉴于需要同时预测大量数据位,作出正确预测的机率非常低。

渗透测试步骤

(1)从第一个应用程序页面到任何登录功能,遍历应用程序,确定其何时以及如何发布会话令牌。以下两种行为较为常见:

只要收到未提交令牌的请求,应用程序就会创建新会话;

应用程序在成功登录后创建新会话。

要想自动收集大量令牌,最好的办法是确定一个导致应用程序发布新令牌的请求(通常为GET/或登录请求)。

(2)在Burp Suite中,向Burp Sequencer发送创建新会话的请求,并配置令牌的位置。然后启动实时捕获,收集尽可能多的令牌。如果应用程序采用了定制会话管理机制,则只能远程访问该应用程序,你可以尽可能迅速地收集令牌,以尽量防止丢失发布给其他用户的令牌,并降低时间依赖造成的影响。

(3)如果应用程序采用商业会话管理机制并且/或者你可以本地访问该应用程序,则可以在受控条件下获得无数会话令牌。

(4)在Burp Sequencer收集令牌的同时,启动“自动分析”(auto analyse)设置,使Burp定期自动执行统计分析。至少收集500个令牌,然后详细审查分析结果。如果令牌中有足够的数据位通过了测试,继续尽可能长时间地收集令牌,并在收集其他令牌时审查分析结果。

(5)如果令牌未通过随机性测试,并且似乎包含可用于预测将来令牌的模式,则可以从另一个IP地址、使用不同的用户名(如果相关)重新开始收集令牌的操作。这有助于确定是否能够发现相同的模式,以及是否能够根据前一次操作获得的令牌进行推断,确定后一次操作得到的令牌。有时候,一个用户收集的令牌序列表现出某种模式,但因为来源IP地址之类的信息被用作熵源(如随机数字发生器的种子),通过这种模式并不能推断出向其他用户发布的令牌。

(6)如果攻击者认为已经充分了解令牌生成算法,能够向其他用户的会话发动自动攻击,通过一段定制脚本实施攻击可能是最好的方法,因为它能够使用观测到的特定模式生成令牌,并应用任何必需的编码。请参阅第14章了解在这种情况下应用某些常规技巧的信息。

(7)如果可以查看源代码,则应仔细检查负责生成会话令牌的代码,了解它使用的机制,并确定是否可以轻易预测该令牌。如果确定能够对从应用程序数据中提取的熵实施蛮力攻击,这时需要考虑对应用程序令牌实施蛮力攻击所需的具体请求数。

尝试访问

http://mdsec.net/auth/361/

加密令牌

一些应用程序使用包含用户有意义信息的令牌,并通过在向用户发布令牌之前对令牌进行加密来避免这种做法导致的明显问题。由于使用了用户未知的密钥对令牌进行加密,这种方法似乎较为稳妥,因为用户无法解密令牌并篡改其内容。

但是,在某些情况下,根据所采用的加密算法以及应用程序处理令牌的方式,用户甚至不需要解密令牌,就可以篡改令牌中有意义的内容。这听起来可能有些匪夷所思,但实际上,这些攻击确实可行,而且有时轻易就可以实施;事实证明,现实世界中的许多应用程序都易于受到这种攻击。不过,这类攻击是否可行,要取决于加密令牌时所采用的具体加密算法。

ECB密码

采用加密令牌的应用程序使用对称加密算法,用于解密用户返回的令牌,恢复令牌中有意义的内容。一些对称加密算法使用“电子密码本”(ECB)密码。这种密码将明文划分成同等大小的分组(如每组8个字节),然后使用密钥加密每个分组。在解密过程中,再使用相同的密钥对每个密文分组进行解密,将其恢复为原始的明文分组。这种加密方法有一个特点,即如果明文分组存在某种模式,这可能导致密文分组也存在一定的模式,因为明文分组与加密后的密文分组完全相同。对于某些类型的数据(如位图图像)而言,这意味着可以从密文判断明文中的有意义信息,如图7-4所示。

图7-4 使用ECB密码加密的明文中的模式可能会在生成的密文中可见

尽管ECB存在上述缺点,但Web应用程序仍然经常使用这类密码来加密信息。即使“明文模式”问题不会出现,这种加密方法仍然存在漏洞。这主要是因为它的明文分组与密文分组完全对应所致。

以下面的应用程序为例,该应用程序的令牌包含几个不同的有意义组件,包括一个数字用户标识符:

对这个令牌进行加密后,它变得没有任何意义,并且可能通过所有标准的随机性统计测试:

ECB密码用于加密8字节的分组,其明文分组与对应的密文分组如下所示:

现在,由于每个密文分组将始终解密成同一个明文分组,因此,攻击者就可以改变密文分组的顺序,以某种有意义的方式修改对应的明文分组。根据应用程序处理生成的加密令牌的具体方式,攻击者可以通过这种方法切换到其他用户,或提升自己的权限。

例如,如果将第二个分组复制到第四个分组之后,将得到如下所示的分组序列:

现在,加密的令牌中包含一个经过修改的uid值,以及一个复制的app值。具体会出现什么情况,将取决于应用程序如何处理加密令牌。通常,以这种方式使用令牌的应用程序仅检查加密令牌的某些部分,如用户标识符。如果应用程序这样处理令牌,那么,应用程序将处理uid为992(而不是最初的218)的用户的请求。

上述攻击能否奏效,取决于你在操纵分组时,应用程序是否发布一个令牌,在其中包含与有效的uid值对应的rnd值。另一种更加可靠的攻击方法,是以适当的偏移值注册一个包含数值的用户名,并复制该分组以替换现有的uid值。假设你注册用户名daf1,并且应用程序发布以下令牌:

该令牌的明文分组和密文分组如下所示:

然后,如果将第七个分组复制到第四个分组之后,加密令牌将包含uid值1:

通过注册适当范围的用户名并重新实施这个攻击,你就可以循环使用所有有效的uid值,从而伪装成每一个应用程序用户。

尝试访问

http://mdsec.net/auth/363/

CBC密码

鉴于ECB密码存在明显的缺陷,于是人们开发出了密码块链(CBC)密码。使用CBC密码时,在加密每个明文分组之前,将它与前一个密文分组进行XOR运算,如图7-5所示。这样,同一个明文分组就不会被加密成同一个密文分组。解密时逆向执行XOR运算,每个解密的分组将与前一个密文分组进行XOR运算,以恢复原始的明文。

图7-5 使用CBC密码时,在加密每个明文分组之前,将它与前一个密文分组进行XOR运算

由于使用CBC密码可以避免使用ECB密码造成的某些问题,因此,CBC模式经常使用标准对称加密算法,如DES和AES。但是,由于Web应用程序经常使用CBC加密的令牌,这意味着攻击者不需要了解密钥就可以操纵解密令牌的某些部分。

下面我们对上一个包含几个不同的有意义组件(包括一个数字用户标识符)的应用程序令牌稍做修改:

和前面一样,加密这段信息后,将生成一个明显无意义的令牌:

由于这个令牌是使用CBC密码加密的,因此,在解密该令牌时,将对每个密文分组与下一个解密的文本块进行XOR运算,以获得明文。现在,如果攻击者修改密文(他收到的令牌)的某些部分,将导致特定的分组被解密成乱码。但是,这也导致下一个解密的文本块将与不同的值进行XOR运算,从而生成经过修改但仍有意义的明文。换言之,通过操纵令牌中的某个分组,攻击者能够修改它之后的分组的解密内容。如果应用程序以危险的方式处理生成的解密令牌,攻击者将能够切换到其他用户或提升自己的权限。

为什么会出现这种情况呢?在上述示例中,攻击者对加密的令牌进行修改,每次以任意方式更改一个字符,并将修改后的令牌发送给应用程序。在这个过程中,攻击者会提出大量请求。应用程序对每个修改后的令牌进行解密后生成的部分值如下所示:

在每一个值中,如我们预料的那样,攻击者修改的分组被解密成乱码(以????????表示)。但是,之后的分组被解密成有意义的文本,只是与原始令牌略有不同。如前所述,出现这种不同,是因为解密的文本与前一个密文分组进行了XOR运算,而攻击者已对该密文分组进行了略微修改。

虽然攻击者看不到解密的值,但应用程序会尝试处理这些值,随后攻击者会在应用程序的响应中看到处理结果。具体来说,接下来会出现什么情况,取决于应用程序如何处理经过修改的解密令牌。如果应用程序拒绝包含任何无效数据的令牌,攻击将会失败。但是,以这种方式使用令牌的应用程序通常仅查看解密令牌的某些部分,如用户标识符。如果应用程序这样处理令牌,那么,上面列表中的第八个分组将能够成功实施攻击,应用程序将处理攻击者提出的请求,并认为用户的uid为226,而不是最初的216。

使用Burp Intrude冲的“位翻转程序”(bit flipper)有效载荷类型,可以轻松测试出应用程序是否存在这方面的漏洞。首先,需要使用你自己的账户登录应用程序。然后,找到一个使用已登录会话并在响应中显示已登录用户标识符的应用程序页面,通常是用户的“主登录”页或“账户详细资料”页。图7-6显示了如何将Burp Intruder设置为针对用户的主页,其中的加密会话令牌被标记为有效载荷位置。

图7-6 将Burp Intruder配置为修改加密会话令牌

所需的有效载荷配置如图7-7所示。它指示Burp处理令牌的原始值,将其视为ASCII编码的十六进制代码,并“翻转”每个字符位置的每个数据位。这种方法非常理想,因为只需要提交较小数量的请求(令牌中的每个字节8个请求),并且几乎总是能够确定应用程序是否易于受到攻击。这样就可以采用更有针对性的攻击,对应用程序的漏洞加以利用。

图7-7 将Burp Intruder配置为“翻转”加密令牌中的每个位

实施攻击时,最初的请求并不会导致应用程序的响应产生任何明显的变化,用户的会话也未遭到修改。这本身就值得我们注意,因为这表示令牌的第一部分并未用于确定登录用户。攻击过程中随后提交的许多请求导致应用程序重定向到登录页面,这说明所做的修改已导致令牌失效。关键在于,还存在一些请求,其响应似乎是有效会话的一部分,但实际上与原始的用户标识符无关。这些请求与包含uid的令牌分组对应。某些情况下,应用程序仅显示“未知用户”,表示经过修改的uid没有对应的实际用户,因此攻击将会失败。其他情况下,应用程序会显示其他注册用户的名称,这说明攻击已取得成功。攻击结果如图7-8所示。我们在其中定义了一个“提取grep”列来显示登录用户的标识符,并设置过滤器来隐藏将应用程序重定向到登录页面的响应。

图7-8 攻击加密令牌的成功“位翻转”攻击

确定存在的漏洞后就可以通过更有针对性的攻击来利用该漏洞。为此,需要从结果中确定,如果用户的资料发生变化,加密令牌中的哪个数据块会发生变化。然后,再实施攻击以测试这个数据块中的其他值。这时,可以使用Burp Intrude冲的“数字”有效载荷类型。

尝试访问

http://mdsec.net/auth/365/

 注解 一些应用程序经常加密请求参数中的有意义数据(如商品的价格),以防止攻击者篡改这些数据。如果加密数据明显在应用程序的功能中发挥着重要作用,这时应尝试实施“位翻转”攻击,看是否能够以有意义的方式操纵加密信息,从而达到破坏应用程序逻辑的目的。

当然,利用本节所述的漏洞的主要目的是伪装成其他应用程序用户——最好是拥有较高权限的管理用户。如果只能盲目修改加密令牌的某些部分,则攻击能否取得成功,只能靠运气。但是,某些情况下,应用程序可能会为你提供一些“提示”。如果应用程序采用对称加密防止用户篡改数据,则通常整个应用程序都会使用相同的加密算法和密钥。在这种情况下,如果应用程序的任何功能向用户披露了任意加密字符串的解密值,则可以利用这一 “提示”完全解密任何受保护的信息。

以笔者发现的一个提供文件上传/下载功能的应用程序为例。上传文件后,用户将获得一个包含文件名参数的下载链接。为防止修改文件路径的各种攻击,应用程序对这个参数中的文件名进行加密。但是,如果用户请求一个已删除的文件,应用程序将显示一条错误消息,在其中显示所请求文件的解密名称。攻击者对这种行为加以利用就可以确定应用程序使用的任何加密字符中的明文值,包括会话令牌的值。笔者发现,该应用程序的会话令牌包含各种有意义的值(具有结构化的格式),因此,易于受到本节所述的各种攻击。由于这些值属于文本格式的用户名和应用程序角色,而不是数字标识符,因此,仅仅使用盲目的位翻转很难对其实施成功的攻击。但是,通过使用文件名解密函数就可以在查看结果的同时,对令牌的数据位进行系统地修改。这样就可以构造一个令牌,该令牌一旦解密将指定有效的用户和管理角色,从而完全控制整个应用程序。

 注解 还可以使用其他技巧来解密应用程序使用的加密数据。例如,利用一个“揭示性”加密提示(“reveal” encryption oracle)可获得加密令牌的明文值。虽然在解密密码时,这可能是一个重要的漏洞,但是,解密会话令牌并不会使攻击者能够立即攻破其他用户的会话。不过,解密令牌有助于攻击者了解明文的结构,帮助他们实施针对性的“位翻转”攻击。有关“揭示”加密提示攻击的详细信息,请参阅第11章。

针对填充提示(padding oracle)的旁路攻击可用于破解加密令牌。请参阅第18章了解详细信息。

渗透测试步骤

许多时候,在应用程序采用加密令牌时,具体的攻击方法可能要取决于各种因素,包括分组边界相对于攻击的数据的偏移值,以及应用程序是否允许修改明文的结构。完全盲目地开始攻击将很难实施有效的攻击,但在许多情况下,这种攻击也可能会奏效。

(1)除非会话令牌明显有意义或本身是连续的,否则应始终考虑令牌被加密的可能性。通常,通过注册几个不同的用户名,并在用户名中每次添加一个字符,就可以确定应用程序是否采用分组密码。如果添加一个字符会导致会话令牌的长度增加8或16个字节,这说明应用程序可能使用的是分组密码。要确认这一点,可以继续在用户名中添加字节,看随后会话令牌是否会同样增加8或16个字节。

(2)通常,在盲目的情况下,很难确定并利用ECB密码操纵漏洞。但是,可以尝试盲目地复制并移动令牌中的密文分组,并查看能否能够以自己的用户账户或其他用户账户登录应用程序,或根本无法登录应用程序。

(3)你可以通过使用“位翻转”有效载荷源对整个令牌实施Burp Intruder攻击,测试应用程序是否存在CBC密码操纵漏洞。如果位翻转确定了令牌中的某个部分,并且修改该部分后仍然能够以其他用户或不存在的用户身份访问有效会话,那么,攻击者可以扩大攻击范围,对这个部分中的每个值进行测试,以实施更有针对性的攻击。

(4)在上述两种攻击过程中,监视应用程序的响应,确定与提交的每个请求生成的会话有关的用户,并尝试利用任何机会来提升自己的权限。

(5)如果攻击并未奏效,但通过步骤1得知,攻击者提交的长度可变的输入已被合并到令牌中,这时,应尝试每次增加一个字符,直到达到所使用的分组的大小,以生成一系列令牌。然后,对于每个生成的令牌,应执行步骤(2)和步骤(3)。这将增加需要修改的数据恰好与分组边界对齐的机率,帮助你实施成功的攻击。

会话令牌处理中的薄弱环节

不管应用程序如何确保它生成的令牌不包含任何有意义的信息,并且很难加以分析或预测,但如果生成令牌后不对其小心处理,它的会话机制仍然易于受到各种攻击。例如,如果以某种方式将令牌透露给攻击者,那么即使攻击者无法预测令牌,也仍然能够劫持用户的会话。

应用程序以不安全的方式处理令牌,致使令牌易于遭受多种攻击。

 错误观点 “因为使用SSL,所以我们的令牌十分安全,不会泄露给第三方。”

正确使用SSL确实有助于防止会话令牌被拦截。但是,即使采用SSL,我们犯下的各种错误仍然可导致令牌以明文形式传输。因此,攻击者可对终端用户实施各种直接攻击,获得他们的令牌。

 错误观点 “我们的令牌由采用成熟、可靠加密技术的平台生成,因此不可能被攻破。”

默认情况下,应用程序服务器会在用户初次访问站点时创建一个会话cookie,并将此cookie用于用户与站点的所有交互。我们在下面的章节中将讲到,这可能会导致令牌处理方面的各种安全漏洞。

在网络上泄露令牌

如果通过网络以非加密方式传送会话令牌,就会产生这方面的漏洞,允许处在适当位置的窃听者能够截获令牌并因此伪装成合法用户。窃听的适当位置包括用户的本地网络、用户所在的IT部门、用户的ISP、因特网骨干网、应用程序的ISP和运行应用程序组织的IT部门。处在上述任何一个位置,相关组织的授权人员和任何攻破相关基础架构的外部攻击者都可以截取会话令牌。

在最简单的情形中,应用程序使用一个非加密的HTTP连接进行通信。这使攻击者能够拦截客户端和服务器间传送的所有数据,包括登录证书、个人信息、支付细节等。这时,攻击者通常不必攻击用户的会话,因为攻击者已经可以查阅特权信息,并能够使用截获的证书登录,从而执行其他恶意操作。然而,有些时候,用户的会话仍然是攻击者的主要攻击目标。例如,如果截获的证书不足以执行第二次登录(如银行应用程序可能要求登录者提交在不断变化的物理令牌上显示的一串数字,或者用户PIN号码中的几个特殊数字),这时攻击者如果想执行任意操作,就必须劫持他窃听的会话。或者如果登录机制实施严格的审查,并且在每次成功登录后通知用户,那么攻击者可能希望避免自己登录,以尽可能保持活动的隐秘性。

在其他情况下,使用HTTPS保护关键客户端-服务器通信的应用程序的会话令牌仍然可能在网络上遭到拦截。这种薄弱环节表现形式各异,其中有许多可能发生在应用程序使用HTTP cookie作为会话令牌传输机制时。

一些应用程序在登录阶段选择使用HTTPS保护用户证书的安全,但在用户会话的其他阶段转而使用HTTP。许多Web邮件应用程序以这种方式运作。在这种情况下,窃听者无法拦截用户的证书,但仍然可以截获会话令牌。Firefox的插件Firesheep工具会让这一过程变得轻而易举。

一些应用程序在站点中预先通过验证的区域(如站点首页)使用HTTP,但从登录页面开始转换到HTTPS。然而,许多时候,应用程序在用户访问第一个页面时就给他发布一个会话令牌,并且在用户登录时也不修改这个令牌。最初并未通过验证的用户会话在登录后却被升级为通过验证的会话。在这种情况下,窃听者就可以在登录前拦截用户的令牌,等待用户转换到HTTPS进行通信(表示用户正在登录)然后尝试使用那个令牌访问一个受保护的页面(如“我的账户”)。

即使应用程序在用户成功登录后发布一个新令牌,并从登录页面开始使用HTTPS,但是,如果用户通过单击验证区域中的一个链接、使用“后退”按钮或者直接输入URL,重新访问一个预先验证的页面(如“帮助”或“关于”页面),用户通过验证的会话令牌仍有可能泄露。

与前面的情况稍有不同,应用程序可能在用户单击登录链接后转换到HTTPS。然而,如果用户对URL进行相应修改,应用程序仍然接受通过HTTP进行登录。这时,处在适当位置的攻击者就可以修改站点预先通过验证的区域返回的页面,使登录链接指向一个HTTP页面。即使应用程序在用户成功登录后发布一个新令牌,如果攻击者已成功将用户的链接降级为HTTP,他仍然能够拦截这个令牌。

一些应用程序对应用程序内的全部静态内容(如图像、脚本、样式表和页面模板)使用HTTP。如果出现这种行为,用户的浏览器将显示一条警告消息,如图7-9所示。当浏览器显示此警告时,它已经通过HTTP获取了相关数据项,因而已经传送了会话令牌。浏览器显示警告是为了让用户拒绝处理已通过HTTP接收到并因此可能受到污染的响应数据。如前所述,如果用户的浏览器通过HTTP访问一个资源,并使用这个令牌通过HTTPS访问站点中受保护的非静态区域,攻击者就能拦截该用户的会话令牌。

图7-9 如果一个通过HTTPS访问的页面包含通过HTTP访问的数据,浏览器将显示一条警报消息

即使应用程序在每一个页面(包括站点中未通过验证的区域和静态内容)都使用HTTPS,有些情况下,用户的令牌仍然通过HTTP传送。如果攻击者能够以某种方式诱使用户通过HTTP提出一个请求(或者是请求相同服务器上运行的HTTP服务,或者是访问http://server:443/),那么用户就可能在这个过程中提交令牌。这时,攻击者可以采用的攻击手段包括在一封电子邮件或即时消息中给用户发送一个URL,在他控制的一个Web站点中插入一个自动加载的链接,或者使用可单击的横幅广告。(请参阅第12章和第13章了解使用这种技巧对其他用户实施攻击的详细内容。)

渗透测试步骤

(1)以正常方式访问应用程序,从第一个进入点(“起始”URL)开始,接着是登录过程,然后是应用程序的全部功能。记录每一个被访问的URL以及收到新会话令牌的每种场合。特别注意登录功能及HTTP与HTTPS通信之间的转换。可以使用网络嗅探器(如Wireshark)手动或使用拦截代理服务器中的日志功能部分自动完成这一任务,如图7-10所示。

图7-10 遍历应用程序,确认收到新会话令牌的位置

(2)如果应用程序使用HTTP cookie传送会话令牌,那么应确认其是否设置了 secure标记,防止它们通过非加密连接传送令牌。

(3)在正常使用应用程序的情况下,确定会话令牌是否通过非加密连接传送。如果确实如此,应将其视为易于受到拦截。

(4)如果起始页面使用HTTP,然后在登录和站点中通过验证的区域转换到HTTPS,确认应用程序是否在用户登录后发布一个新令牌,或者在使用HTTP阶段传送的令牌是否仍被用于追踪用户通过验证的会话。同时确认,如果对登录URL进行相应修改,应用程序是否接受通过HTTP登录。

(5)即使应用程序在每一个页面使用HTTPS,确认服务器是否还监听80端口,通过它运行任何服务或内容。如果是这样,直接使用通过验证的会话访问所有HTTP URL,确认会话令牌是否被传送。

(6)如果通过HTTP将通过验证会话的令牌传送给服务器,确认该令牌是否继续有效,或者立即被服务器终止。

尝试访问

http://mdsec.net/auth/369/

http://mdsec.net/auth/372/

http://mdsec.net/auth/374/

在日志中泄露令牌

除在网络通信中明文传送会话令牌外,各种系统日志也常常将令牌泄露给未授权方。虽然这种情况很少发生,但由于除了处在网络适当位置的窃听者之外,还有其他各种潜在的攻击者都能查阅这些日志,这种泄露通常会造成更严重的后果。

许多应用程序为管理员和其他支持人员提供监控和控制应用程序运行时状态(包括用户会话)的功能。例如,帮助用户解决疑难的服务台工作人员可能会要求用户提供用户名、通过列表或搜索功能定位他们当前的会话,并查看与会话有关的细节。或者管理员可能会在调查一起违反安全事件的过程中查询最近会话的日志记录。通常,这种监控和控制功能会泄露每个会话的令牌。而且,这种功能一般没有得到良好的保护,允许未授权用户访问当前会话令牌列表,因而劫持所有应用程序用户的会话。

会话令牌出现在系统日志中的另一个主要原因是应用程序使用URL查询字符串,而不是使用HTTP cookie或POST请求主体作为令牌传输机制。例如,在Google上查询inurl:jsessionid即可确定数千个在以下URL中传送Java平台会话令牌(称作jsessionid)的应用程序:

http://www.webjunction.org/do/Navigation;jsessionid=

F27ED2A6AAE4C6DA409A3044E79B8B48?category=327

如果应用程序以这种方式传送会话令牌,它们的会话令牌就可能出现在各种未授权用户能够访问的系统日志中,例如:

用户浏览器的日志;

Web服务器日志;

企业或ISP代理服务器日志;

任何在应用程序主机环境中采用的反向代理的日志;

应用程序用户通过单击站外链接访问的任何服务器的Referer日志,如图7-11所示。

图7-11 当会话令牌出现在URL中时,只要用户点击站外链接或浏览器加载站外资源,会话令牌就会在Referer消息头中传送

即使整个应用程序都使用HTTPS,这些漏洞仍有可能出现。

上面描述的最后一种情况为攻击者提供了一种截获应用程序会话令牌的非常有效的方法。例如,如果一个Web邮件应用程序在URL中传送会话令牌,那么攻击者就可以向应用程序的用户发送一封电子邮件,在里面包含一个连接到他控制的Web服务器的链接。如果任何用户访问这个链接(如单击它,或者他们的浏览器加载了包含在HTML格式的电子邮件中的图像),攻击者就会实时收到该用户的会话令牌。然后,攻击者只需在他的服务器上运行一段脚本,就可以劫持每一个令牌的会话,并执行某种恶意操作,如发送垃圾邮件、获取个人信息或修改密码。

 注解 追踪通过HTTPS访问的页面中包含的站外链接时,当前版本的Internet Explorer中并不包含Referer消息头。在这种情况下,只要站外链接也通过HTTPS访问,即使它属于另一个域,Firefox中也包含Referer消息头。因此,即使使用SSL,插入在URL中的敏感数据也容易在Referer日志中泄露。

渗透测试步骤

(1)确定应用程序的所有功能,找出可查看会话令牌的任何日志或监控功能。查明谁能够访问这种功能,例如管理员、任何通过验证的用户或匿名用户。请参阅第4章了解发现在主应用程序中没有建立直接链接的隐藏内容的技巧。

(2)确定应用程序使用URL中传送会话令牌的任何情况。可能应用程序通常以更加安全的方式传送令牌,但开发者在特定情况下使用URL来解决特殊难题。例如,Web应用程序与外部系统交互时通常会出现这种情况。

(3)如果应用程序在URL中传送会话令牌,尝试发现任何允许在其他用户查阅的页面中注入任意站外链接的应用程序功能,例如公告牌、站点反馈、问与答等功能。如果可以,向一个受控制的Web服务器提交链接,等待一段时间,看Referer日志中是否收到任何用户的会话令牌。

(4)如果截获到任何会话令牌,尝试通过正常使用应用程序,而不是用截获的令牌代替自己的令牌来劫持用户的会话。可以通过拦截服务器返回的下一个响应,并使用截获的cookie值添加自己的Set-Cookie消息头,以实现这一目的。在Burp中,可以应用一个Suite范围的配置,在所有指向目标应用程序的请求中设置一个特殊cookie,以便测试期间在不同的会话之间轻松切换。

(5)如果截获到大量令牌,并且通过劫持用户的会话可以访问他们的敏感数据(如个人资料、支付信息或用户密码),就能够使用第14章描述的自动技巧获得想要的、属于其他应用程序用户的数据。

尝试访问

http://mdsec.net/auth/379/

令牌–会话映射易受攻击

由于应用程序在将生成和处理的会话令牌与令牌所属的用户会话之间进行对应的过程中存在薄弱环节,会话管理机制因此存在各种常见的漏洞。

最简单的漏洞是允许给同一个用户账户同时分配几个有效的令牌。在几乎每一个应用程序中,任何用户都没有正当理由在任何指定的时间拥有多个会话。当然,用户可以终止一个处于活动状态的会话,再开始一个新会话,这很常见,例如,关闭浏览器窗口或转换到另一台计算机上。但是,如果一名用户明显同时在使用两个不同的会话,这通常表示出现了安全问题:要么是因为用户将证书泄露给了第三方,要么是攻击者通过其他某种途径获得了他的证书。无论发生哪一种情况,都不应允许并行会话,因为它允许用户持续执行任何非法操作,同时允许攻击者使用截获的证书,却不存在被检测出来的风险。

应用程序使用“静态”令牌是一种相对较为特殊的缺陷。这些“令牌”看似会话令牌,最初表现的功能和会话令牌一样,但实际并非如此。在这些应用程序中,每名用户都分配有一个令牌,并且用户每次登录,都收到相同的令牌。无论用户是否已经登录并获得令牌,应用程序都应将该令牌视为有效令牌。这种应用程序实际上是对会话的整体概念以及这样做有助于管理和控制应用程序访问存有误解。有些时候,应用程序这样运作,是为了执行设计上存在缺陷的“记住我”功能,并因此将静态令牌保存在一个持久性cookie中(请参阅第6章了解相关内容)。有时,令牌本身易于受到预测攻击,致使这种漏洞造成更加严重的后果,因为一次成功的攻击不仅能够攻破当前登录用户的会话,如果时间允许,还能攻破所有注册用户的账户。

我们偶尔也会观察到应用程序表现出其他奇怪的行为,表明令牌和会话之间的对应关系存在基本的缺陷。根据用户名和一个随机成分构造的有意义令牌就是一个典型的示例。以下面的令牌为例:

对其进行Base64编码,得到:

对成分r1进行仔细分析后,我们可以得出结论:根据样本值无法对其进行预测。然而,如果应用程序的会话处理逻辑存在缺陷,可能攻击者只需向r1和user提交任何有效的值,就可以在指定用户的权限下访问一个会话。从本质上讲,这是一个访问控制漏洞,因为应用程序是根据用户在会话之外提交的数据做出的访问决定(请参阅第8章了解相关内容)。产生这种漏洞是因为应用程序使用会话令牌表明请求者已经与应用程序建立某种有效的会话;然而,处理会话的用户权限并不由会话控制,而是通过其他方式根据每个请求决定。在这种情况下,请求者直接控制决定用户权限的方式。

渗透测试步骤

(1)用相同的用户账户从不同的浏览器进程或从不同的计算机两次登录应用程序。确定这两个会话是否都处于活动状态。如果是,表示应用程序支持并行会话,这样攻破其他用户证书的攻击者能够利用这些证书,而不会有被检测出来的风险。

(2)用相同的用户账户从不同的浏览器进程或从不同的计算机几次登录和退出应用程序。确定应用程序在每次登录时是发布一个新会话令牌,还是发布相同的令牌。如果每次发布相同的令牌,那么应用程序根本没有正确使用令牌。

(3)如果令牌明显包含某种结构和意义,设法将标识用户身份的成分与无法辨别的成分区分开来。尝试修改所有与用户有关的令牌成分,使其指向其他已知的应用程序用户,确定修改后的令牌是否被应用程序接受,以及是否能够让攻击者伪装成那名用户。

尝试访问

http://mdsec.net/auth/382/

http://mdsec.net/auth/385/

会话终止易受攻击

由于两方面的原因,正确终止会话非常重要。首先,尽可能缩短一个会话的寿命可降低攻击者截获、猜测或滥用有效会话令牌的可能性。其次,如果用户不再需要现有会话,终止会话为用户提供一种使其失效的途径,在进一步降低上述可能性的同时,在某种程度上确保共享计算环境中会话的安全。会话终止功能的主要缺陷大都与无法满足这两个关键目标有关。

一些应用程序并不实施有效的会话终止功能。会话一旦建立,它在收到最后请求后的许多天内也仍然有效,直到服务器最终将其清除。即使令牌存在某种非常难以利用的缺陷(例如,确定每个有效令牌需要100 000次猜测),攻击者仍然能够截获最近访问应用程序的每一名用户的令牌。

一些应用程序并不提供有效的退出功能。

有些时候,应用程序根本不执行退出功能。用户没有办法要求应用程序终止他们的会话。

有些时候,退出功能实际上并不能帮助服务器终止会话。即使服务器从用户的浏览器中删除令牌(例如,通过发布一个清空令牌的Set-Cookie指令)。然而,如果用户继续提交这个令牌,服务器仍然接受它。

最糟糕的情况:当用户单击“退出”按钮时,应用程序并不与服务器通信,因此服务器不采取任何行动。相反,应用程序执行一段客户端脚本清空用户的cookie,在随后的请求中将用户返回到登录页面。访问这个cookie的攻击者就能使用会话,好像用户从未退出一样。

渗透测试步骤

(1)不要掉入检查应用程序对客户端令牌执行的操作(如通过一个新的Set-Cookie指令、客户端脚本或者终止时间属性令cookie失效)的陷阱。在客户端浏览器内对令牌执行的任何操作并不能终止会话。相反,调查服务器端是否执行会话终止操作。

(a)登录应用程序获得一个有效令牌。

(b)不使用这个令牌,等待一段时间后,使用这个令牌提交一个访问受保护页面(如“我的资料”页面)的请求。

(c)如果该页面正常显示,表示令牌仍然处于活动状态。

(d)使用反复试验的方法确定会话终止超时时间,或者一个令牌在最后一次使用它提交请求几天后是否仍被使用。可配置Burp IntruderM增连续请求之间的时间间隔,自动完成这项任务。

(2)确定是否存在退出功能,用户是否能够使用这一功能。如果不能,表示用户更易于受到攻击,因为他们没有办法让应用程序终止会话。

(3)如果应用程序提供退出功能,测试其效率。退出后,尝试重新使用原有的令牌,确定其是否仍然有效。如果令牌仍然有效,那么即使用户已经“退出”,也依然易于受到会话劫持攻击。可以使用Burp Suite测试此功能的效率,具体操作如下:从代理服务器历史记录中选择一个依赖会话的最近请求,在从应用程序中注销后将其发送给Burp Repeater重新发布该请求。

尝试访问

http://mdsec.net/auth/423/

http://mdsec.net/auth/439/

http://mdsec.net/auth/447/

http://mdsec.net/auth/452/

http://mdsec.net/auth/457/

客户端暴露在令牌劫持风险之中

攻击者可以采用各种方法向应用程序的其他用户发动攻击,试图截获或滥用他们的会话令牌。

攻击者可通过跨站点脚本攻击查询用户的cookie,获得他们的会话令牌,然后将其传送至自己控制的任意服务器。第12章将详细介绍这种攻击。

攻击者可以使用其他针对用户的攻击,以不同的方式劫持用户的会话。这包括实施会话固定攻击,即攻击者向一名用户发送一个已知的会话令牌,等待他登录,然后劫持他的会话;以及跨站点请求伪造攻击,其中攻击者从他控制的一个Web站点向应用程序提出一个专门设计的请求,由于用户的浏览器会随同这个请求一起自动提交用户当前的cookie,攻击者因此会获得用户的cookie。这些攻击也请参阅第12章的介绍。

渗透测试步骤

(1)确定应用程序中存在的任何跨站点脚本漏洞,看是否可以利用这些漏洞截获其他用户的会话令牌(请参阅第12章了解相关内容)。

(2)如果应用程序向未通过验证的用户发布令牌,就会获得一个令牌并进行一次登录。如果应用程序在攻击者登录后并不发布一个新令牌,就表示它易于受到会话固定攻击。

(3)即使应用程序并不向未通过验证的用户发布会话令牌,仍然会通过登录获得一个令牌,然后返回登录页面。如果应用程序“愿意”返回这个页面,即使攻击者已经通过验证,那么也可以使用相同的令牌以另一名用户的身份提交另一次登录。如果应用程序在第二次登录后并不发布一个新令牌,表示它易于受到会话固定攻击。

(4)确定应用程序会话令牌的格式。用一个格式有效的伪造值修改令牌,然后尝试使用它登录。如果应用程序允许使用一个捏造的令牌建立一个通过验证的会话,表示它易于受到会话固定攻击。

(5)如果应用程序并不支持登录功能,但处理敏感数据(如个人信息和支付细节),并在提交后显示这些信息(如在“确认订单”页面上),那么使用前面的三种测试方法尝试访问显示敏感数据的页面。如果在匿名使用应用程序期间生成的令牌可用于获取用户的敏感信息,那么应用程序就易于遭受会话固定攻击。

(6)如果应用程序完全依靠HTTP cookie传送会话令牌,它很可能容易受到跨站点请求伪造(CSRF)攻击。首先登录应用程序。然后,从另一个应用程序的页面向应用程序提出一个请求,解认它是否会提交用户的令牌。(必须从与登录目标应用程序相同的浏览器进程窗口提交令牌。)设法确定所有参数可由攻击者提前决定的应用程序敏感功能,利用这种缺陷在目标用户的权限下执行未授权操作。请参阅第13章了解实施CSRF攻击的详情。

宽泛的cookie范围

cookie的工作机制可简单概括如下:服务器使用HTTP响应消息头Set-cookie发布一个cookie,然后浏览器在随后的请求中使用Cookie消息头向同一台服务器重新提交这个cookie。事实上,事情远比这复杂。

cookie机制允许服务器指定将每个cookie重新提交到哪个域和哪个URL路径。为完成这一任务,它在Set-cookie指令中使用domain和path属性。

cookie域限制

位于foo.wahh-app.com的应用程序建立一个cookie后,浏览器会默认在随后的所有请求中将 cookie 重新提交到 foo.wahh-app.com 及任何子域(如 admin.foo.wahh-app.com)中。它不会将cookie提交给其他任何域,包括父域wahh-app. com和父域的其他任何子域,如bar.wahh-app.com。

服务器可以在Set-cookie指令中插入一个domain属性,以改变这种默认行为。例如,假设位于foo.wahh-app.com的应用程序返回以下HTTP消息头:

浏览器会将这个cookie重新提交给wahh-app.com的所有子域,包括bar.wahh-app.com。

 注解 服务器不能使用这个属性随意指定域。首先,指定的域要么必须是应用程序在其上运行的域,要么是它的父域(或为直接父域,或有一定间隔)。其次,指定的域不能为.com或.co.uk之类的顶级域,因为这样做会允许恶意服务器在其他任何域上建立任意cookie。如果服务器违反以上任何一条规定,浏览器将完全忽略Set-cookie指令。

如果应用程序将cookie范围设定得过于宽泛,也可能会使应用程序出现各种安全漏洞。

以一个允许用户注册、登录、写博客文章、阅读他人博客的博客应用程序为例。它的主应用程序位于域wahh-blogs.com上,当用户登录这个应用程序时,他会从一个以这个域为范围的cookie中收到会话令牌。每名用户都可以创建自己的博客,通过以用户名为前缀的一个新的子域进行访问,例如:

因为cookie被自动重新提交到这个范围内的每一个子域,当一名已经登录的用户浏览其他用户的博客时,他的会话令牌将与其请求一起提交。如果允许博客作者在他们自己的博客中插入任意JavaScript脚本(就像现在的许多博客应用程序那样),那么一个恶意博客作者就能够以和保存型跨站点脚本攻击一样的方式窃取其他用户的会话令牌(请参阅第12章了解相关内容)。

之所以会出现这样的问题,是因为用户创作的博客是处理验证和会话管理的主应用程序的子域。HTTP cookie并没有能力帮助应用程序防止主域发布的cookie被重新提交给它的子域。

要解决这个问题,主应用程序可以使用一个不同的域名(如www.wahh-blogs.com),并以这个完全合格的域名为它的会话令牌cookie的域范围。这样,如果登录用户浏览其他用户的博客,会话cookie就不会被提交。

如果一个应用程序明确以一个父域作为它的cookie域范围,就会出现这种漏洞的另一个版本。例如,假设一个安全性至关重要的应用程序位于域sensitiveapp.wahh-organization.com上,当它建立cookie时,它自由设置的域范围如下:

这样做造成的后果是:当用户访问wahh-organization.com使用的每一个子域时,机密应用程序的会话令牌cookie都将被提交。这些子域包括:

虽然这些应用程序可能全都属于拥有机密应用程序的同一组织,但由于以下原因,不应将敏感应用程序的cookie提交给其他应用程序。

负责其他应用程序的人员与负责机密应用程序的人员的信任级别不同。

与前面的博客应用程序一样,其他应用程序的功能可能会将提交给应用程序的cookie值泄露给第三方。

其他应用程序可能并不像机密应用程序那样遵循同样严格的安全标准,或者接受全面的安全测试(因为它们不够重要、并不处理敏感数据,或者仅为测试目的而建立)。应用程序中存在的许多漏洞(例如跨站点脚本漏洞)可能不会影响它们的安全状况,但外部攻击者却可以利用一个不安全的应用程序截获由机密应用程序创建的会话令牌。

 注解 通常而言,基于域的cookie隔离并不像同源策略(请参阅第3章)那样严格。除了在处理主机名时讨论的问题外,浏览器还会在确定cookie范围时忽略协议和端口号。如果某个应用程序与另一个不可信的应用程序共享主机名,并依赖协议或端口号中的差异来隔离自身,那么,攻击者通过处理cookie即可轻而易举地破坏这种隔离。该应用程序发布的任何cookie都可被与它共享主机名的不可信应用程序访问。

渗透测试步骤

审查应用程序发布的所有cookie,检查用于控制cookie范围的任何domain属性。

(1)如果应用程序将cookie范围明确放宽到父域,则该应用程序可能易于受到通过其他Web应用程序实施的攻击。

(2)如果应用程序将它的cookie范围设置为自己的域名(或者并未指定domain属性),则通过它的子域仍然可以访问其中的应用程序或功能。

确定将收到应用程序发布的cookie的所有可能的域名。如果通过这些域名可以访问任何其他Web应用程序或功能,确定是否可以利用它们获得目标应用程序的用户发布的cookie。

2.cookie路径限制

位于/apps/secure/foo-app/index.jsp的应用程序建立一个cookie后,浏览器会默认在随后的所有请求中将cookie重新提交到路径/apps/secure/foo-app/及任何子目录。它不会将cookie提交到父目录或服务器上的其他任何目录路径。

与cookie范围域限制一样,服务器可以在Set-cookie指令中插入一个path属性,改变这种默认行为。例如,如果应用程序返回以下HTTP消息头:

那么浏览器会将这个cookie重新返回到/apps/路径的所有子目录中。

相比于基于域的cookie范围,这种基于路径的限制比同源策略更加严格。因此,如果将其作为一种安全机制,用于防范同一域中的不可信应用程序,则这种防御机制几乎完全无效。在某个路径上运行的客户端代码能够打开指向同一域上不同路径的窗口或iframe,并能够读取或写入该窗口,而不受任何限制。因此,获取范围为同一域上的其他路径的cookie实际上并不困难。有关更多详情,请参阅以下Amit Klein撰写的论文:

保障会话管理的安全

鉴于会话管理机制主要受两类漏洞的影响,Web应用程序必须采取相应的防御措施,防止这些机制受到攻击。为安全地执行会话管理,应用程序必须以可靠的方式生成令牌,并且必须在令牌生成到废止的整个生命周期中确保它们的安全。

生成强大的令牌

用于在连续请求中重新标识用户身份的令牌,在其生成过程中,不应给攻击者提供任何机会,使其能够以常规方式预测或推断发布给其他用户的令牌,从而从应用程序中获得大量的令牌样本。

最有效的令牌生成机制应当具备以下两点:

使用数量极其庞大的一组可能值;

包含强大的伪随机源,确保令牌以无法预测的方式平均分布在可能值范围内。

从理论上讲,只要拥有足够的时间和资源,任何数据,无论其长度和复杂程度如何,都可以使用蛮力猜测出来。设计强大的令牌生成机制的目的在于:即使蓄意破坏的攻击者拥有大量带宽和处理资源,他也绝无可能在令牌的有效期限内,成功猜测出任何一个有效的令牌。

除服务器用来定位处理用户请求的相关会话对象的一个标识符外,令牌中不应包含其他任何内容。无论是公开显示还是隐藏在几层编码或模糊处理中,令牌都不应含有意义或采用结构。所有关于会话所有者与状态的数据都应保存在与会话令牌对应的服务器会话对象中。

应谨慎选择随机源。开发者应当认识到,各种可用的随机源在强度上可能存在巨大的差异。和java.util.Random一样,一些随机源非常适用于各种需要不断变化的输入源的情况,但只需根据唯一一个输出项就可以准确地推断出它的前后随机数。开发者应研究不同的可用随机源实际使用算法的数学特性,并阅读相关文档资料,了解API的推荐用法。一般来说,如果某种算法没有明确说明它具有加密安全性,那么应认为它可被预测。

 注解 由于一些高强度的随机源必须采取步骤获得足够的熵(如从系统事件中等),它们需要一段时间才能返回输入序列中的下一个值,因此可能无法为一些大容量的应用程序迅速建立随机数以生成令牌。

除选择最为稳定可靠的随机源外,以与为其生成令牌请求有关的一些信息作为熵源,也是一种良好的做法。这些信息可能并不是那个请求独有的,但却能够非常有效地消除所使用的核心伪随机数发生器存在的任何缺陷。可被合并的信息包括:

来源IP地址(source IP address)及接收请求的端口号;

请求中的User-Agent消息头;

请求时间(毫秒)。

合并这个熵的最有效公式是建立一个特殊的字符串,连接一个伪随机数、一串上面列出的与请求有关的数据以及一个仅服务器知道并在每次重启时重新生成的机密字符串。然后,使用适当的散列算法(例如,使用SHA-256算法)对这个字符串进行处理,生成一个固定长度、便于管理的字符串,并以它作为令牌。(将最容易发生变化的数据项放在散列输入的开始部分有助于最大化散列算法中的“雪崩”效应[2]。)

 提示 决定生成会话令牌的算法后,一个有用的“思想试验”是想象伪随机源被完全攻破,并总是返回相同的随机数。如果出现这种情况,那么从应用程序中获得大量令牌样本的攻击者能够截获发布给其他用户的令牌吗?使用上面描述的公式,即使攻击者完全了解生成令牌所使用的算法,一般也绝无这种可能。来源IP、端口号、User-Agent消息头和请求时间共同生成一个数目庞大的熵。即使掌握所有这些信息,如果不知道服务器使用的机密字符串,攻击者仍然无法生成对应的令牌。

在整个生命周期保障令牌的安全

建立一个无法预测值的安全令牌后,就必须在这个令牌生成到废止的整个生命周期中保障它的安全,确保不会将其泄露给除令牌用户以外的其他任何人。

令牌只能通过HTTPS传送。任何以明文形式传送的令牌都应视为被“污染”,也就是说,不能确保用户身份不被泄露。如果使用HTTP cookie传送令牌,应将这些cookie标记为安全,以防止用户浏览器通过HTTP传送它们。如果可能,应对每个应用程序页面使用HTTPS,包括静态内容(如帮助页面、图像等)。如果没有可能,并且仍然采用HTTP服务,那么应用程序应将任何访问敏感内容(包括登录页面)的请求重定向到HTTPS服务。帮助页面之类的静态资源一般不属于敏感内容,不需要使用通过验证的会话即可访问;因此,可以通过使用cookie范围指令强化cookie的使用安全,防止在访问这些资源的请求中提交令牌。

绝不能在URL中传送会话令牌,因为这样做易于受到会话固定攻击,并可能使令牌出现在各种日志机制中。有时候,开发者在禁用cookie的浏览器中使用这种技巧执行会话。然而,最好是对所有导航使用POST请求实现这一目的,并将令牌保存在HTML表单隐藏字段中。

应总是执行退出功能。通过它删除服务器上的所有会话资源并终止会话令牌。

会话处于非活动状态一段时间(如10分钟)后,应执行会话终止。会话终止的效果应和用户完全退出的作用完全相同。

应防止并行登录。每次一名用户登录,都应发布一个新会话令牌,同时废止任何属于该用户的现有会话,就好像他已经退出应用程序一样。如果旧令牌被保存一段时间,那么随后收到任何使用该令牌提出的请求,都应向用户发出安全警报,告诉他们会话已被终止,因为他已经从其他位置登录。

如果应用程序包含任何可以查看会话令牌的管理或诊断功能,应对这种功能加以严密保护,以防止未授权的访问。许多时候,这种功能根本没有必要显示会话令牌,相反,它应提供足够的与会话所有者有关的信息,以便于执行任何支持和诊断任务。这样做就不会泄露该用户提交的会话令牌,使攻击者劫持他的会话。

应尽可能限定应用程序会话cookie的域和路径范围。范围过于宽泛的cookie通常是由配置不佳的Web应用程序平台或Web服务器生成的,而不是由应用程序开发者本人生成的。通过应用程序cookie范围中的域名或URL路径,应无法访问其他Web应用程序或不可信的功能。应特别注意用于访问应用程序域名的任何现有子域。有时,为了确保不会造成这种漏洞,必须修改组织所使用的各种应用程序的域和路径命名方案。

应采取特殊措施保护会话管理机制的安全,防止应用程序用户成为各种攻击的目标。

应严格审查应用程序的代码库,以确定并删除任何跨站点脚本漏洞(请参阅第12章了解相关内容)。许多这类漏洞可被用于攻击会话管理机制,特别是保存型(或二阶)XSS攻击,它可对每一种会话滥用与劫持防御造成破坏。

不应接受用户提交、但服务器并不认可的任意令牌。应立即在浏览器中取消该令牌,并将用户返回到应用程序的起始页面。

在执行转账之类的重要操作之前,要求进行两步确认或重新验证可有效防御跨站点请求伪造和其他会话攻击。

不完全依赖HTTP cookie传送会话令牌可防御跨站点请求伪造攻击。使用cookie机制会造成这种漏洞是因为,无论什么原因提出请求,浏览器都会自动提交cookie。如果总是通过HTML表单隐藏字段传送令牌,那么除非攻击者已经知道令牌,否则他就无法建立一个表单,再通过提交该表单执行未授权操作;当然,如果他已经知道令牌,就可以轻易实施劫持攻击。每页面令牌也有助于防止这些攻击(请参阅下一节了解相关内容)。

成功验证后应总是建立一个新的会话,以避免会话固定攻击的影响。如果应用程序并不使用验证机制,但允许提交敏感数据,那么会话固定攻击造成的威胁就更难以解除。一种可能的解决办法是使提交敏感数据的页面序列尽可能短,并且在这个序列的第一个页面建立一个新的会话(如有必要,从现有会话中复制任何需要的数据,如购物车的内容),或者使用每页面令牌(参阅下一节)防止知道第一个页面所使用的令牌的攻击者访问随后的页面。除非完全有必要,否则不得向用户显示个人数据。即使有必要(如显示地址的“确认订单”页面),也不得向用户显示信用卡号码和密码之类的敏感数据,并且应在应用程序的响应中隐藏这些数据。

每页面令牌

应在会话令牌的基础上使用每页面令牌,对会话实施更加严格的控制,更有效地防御或阻断各种会话攻击。使用每页面令牌时,每次用户请求一个应用程序页面(例如,不是图像),应用程序都会建立一个新的页面令牌,并通过cookie或HTML表单隐藏字段将其传送给客户端。用户每次提出一个请求,除通过主会话令牌进行正常确认外,页面令牌还根据最后发布的令牌值进行再次检验。如果出现不匹配的情况,整个会话将被终止。因特网上的许多安全性至关重要的应用程序(如电子银行),都使用每页面令牌来强化对会话令牌机制的保护,如图7-12所示。

图7-12 银行应用程序使用的每页面令牌

虽然使用每页面令牌确实给导航造成一些限制(例如,使用“后退”和“前进”按钮以及多窗口浏览方面),但它能够有效防御会话固定攻击,并确保如果合法用户和攻击者同时使用一个被劫持的会话提出相同的请求,该请求会立即被应用程序终止。每页面令牌还可用于追踪用户的位置和在应用程序中的活动情况,检测出不按预定顺序访问某些功能的企图,并有助于防止某些访问控制缺陷(请参阅第8章了解相关内容)。

日志、监控与警报

应用程序的会话管理功能应与它的日志、监控与警报机制紧密结合,以提供适当的反常行为记录,并帮助管理员在必要时采取防御措施。

应用程序应监控包含无效令牌的请求。除非令牌很容易被预测,否则,攻击者就需要提出大量包含无效令牌的请求,才能成功猜测出应用程序发布给其他用户的令牌,从而在应用程序的日志中留下明显的痕迹。

很难完全阻止针对会话令牌的蛮力攻击,因为我们无法通过禁用特殊用户账户或会话来终止这种攻击。一种可能的防御方法是在收到大量包含无效令牌的请求时将其来源IP地址屏蔽一段时间。然而,如果一个用户的请求来自几个IP地址(如AOL用户),或者几个用户的请求来自同一个IP地址(如执行网络地址转换的代理服务器或防火墙中的用户),这种方法就不能发挥太大的作用。

即使无法立即有效防止针对会话的蛮力攻击,但保留详细的日志并向管理员发出警报仍然可帮助他们对攻击进行调查,并尽其所能采取适当的行动。

只要有可能,应向用户警告与会话有关的反常事件,例如并行登录或明显的劫持攻击(使用每页面令牌检测)。即使用户的令牌已被攻破,这样做也可促使用户进行检查,看是否发生转账之类的未授权操作。

反应性会话终止

会话管理机制可非常有效地防御许多针对应用程序的其他攻击。如果收到用户提交的反常请求(例如,任何包含被修改的隐藏HTML表单字段或URL查询字符串参数的请求、任何包含与SQL注入或跨站点脚本攻击有关的字符串请求,以及任何正常情况下已经被长度限制之类的客户端检查阻止的用户输入),那么一些安全性至关重要的应用程序(如电子银行)会极其迅速地终止用户的会话。

当然,任何使用这类请求可对其加以利用的漏洞都必须从源头进行清除。但是,迫使用户每次提交一个无效请求时都需要进行重新验证,这会显著延长探查应用程序漏洞所需的时间,即使采用自动技巧完成这一任务也是如此。如果残余的漏洞确实存在,其他人就更不可能发现它们。

如果执行这种防御,为方便测试,建议在需要时可轻易将其关闭。如果在对应用程序进行合法渗透测试时,这种防御就像是遇到真正的攻击者那样减缓应用程序的响应速度,那么它的效率就会显著降低。与不使用这种机制相比,使用它很可能会在代码中造成更多的漏洞。

渗透测试步骤

如果所攻击的应用程序使用这种防御机制,渗透测试员可能发现在应用程序中探查各种常见的漏洞非常费时,每次测试失败后都需要再次登录并重新导航到正在分析的位置,因此不久后就会主动放弃攻击。

这种烦恼可以使用自动工具来解除。当使用Burp Intruder发动攻击时,可以使用“获取cookie”(Obtain Cookie)特性在每次测试前重新登录,并使用新的会话令牌(只要应用程序采用单阶段登录机制)。当手动浏览和探查应用程序时,可以通过IBurpExtender接口使用Burp Proxy的扩展性特性。渗透测试员可以建立一个扩展,检测应用程序何时执行强制退出、自动登录到应用程序并将新会话和页面返回给浏览器,同时选择使用弹出消息通知所发生的一切。虽然使用这种方法并不能完全解决这个问题,但在某些情况下能显著减轻它造成的影响。

小结

攻击者在攻击应用程序时,会话管理机制中存在的诸多漏洞为他们提供了大量的攻击目标。鉴于其在多个请求中确定相同用户身份过程中所发挥的重要作用,不完善的会话管理功能通常会成为攻击者“进入应用程序王国的钥匙”。对攻击者来说,进入其他用户的会话当然不错,但劫持管理员的会话会更好,因为这样他往往能够攻破整个应用程序。

现实世界的会话管理功能中往往存在各种各样的缺陷。当应用程序采用定制机制时,其中可能会存在各种薄弱环节,并有无数种攻击方法可供攻击者利用。耐心与坚持不懈是我们从中汲取到的最重要的教训。许多会话管理机制初看起来似乎安全可靠,但仔细分析后却发现它们并不合格。解译应用程序如何生成看似随机的令牌序列,这个过程既费时又费力。但由于这样做通常可获得巨大的回报,因而值得为之花费时间和精力。

问题

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

(1)登录一个应用程序后,服务器建立以下cookie:

一个小时后,再次登录并得到以下cookie:

通过这些cookie,可以得出什么推论?

(2)某个应用程序使用由6个字符组成的数字字母会话令牌和由5个字符组成的数字字母密码。它们全都由某种无法预测的算法随机生成。其中哪一个最有可能成为蛮力猜测攻击的目标?列出影响你做出决策的各种不同因素。

(3)登录位于以下URL的一个应用程序:

服务器建立以下cookie:

然后访问下面的URL。浏览器会将sessionId cookie提交给哪些URL?(选出全部答案。)

(a)https://foo.wahh-app.com/login/myaccount.php

(b)https://bar.wahh-app.com/login

(c)https://staging.foo.wahh-app.com/login/home.php

(d)http://foo.wahh-app.com/login/myaccount.php

(e)http://foo.wahh-app.com/logintest/login.php

(f)https://foo.wahh-app.com/logout

(g)https://wahh-app.com/login/

(h)https://xfoo.wahh-app.com/login/myaccount.php

(4)所针对的应用程序除使用主会话令牌外,还使用每页面令牌。如果收到一个不按顺序发送的每页面令牌,整个会话将被终止。假设发现了某种缺陷,可通过它预测或截获应用程序发布给当前正在访问应用程序的其他用户的令牌。那么能够劫持他们的会话吗?

(5)登录一个应用程序后,服务器建立以下cookie:

单击“退出”按钮后,应用程序执行以下客户端脚本:

通过这种行为,可以得出什么结论?

注释

[1] 在信息论中,熵(entropy)可用作某事件不确定度的量度。信息量越大,体系结构越规则,功能越完善,熵就越小。——译者注

[2] 雪崩效应,加密算法的一种特征,指明文或密钥的少量变化会引起密文的很大变化。——译者注

浙ICP备11005866号-12