6.4 对会话管理进行安全防范
在一般情况下,Web开发人员在开发过程中是会留意常规的Web安全漏洞的,但也有一些鲜为人知的漏洞广泛存在于Web应用程序中。大多数开发人员针对这些漏洞不做任何考虑,使得Web应用程序仍然处于危险中。鉴于会话管理机制主要受两类漏洞的影响,Web应用程序必须采取相应的防御措施,防止这些机制受到攻击。为执行安全的会话管理,应用程序必须以可靠的方式生成令牌,并且必须在令牌生成到废止的整个生命周期中确保它们的安全。
6.4.1 设置最有效的令牌生成机制
从理论上讲,只要拥有足够的时间和资源,任何数据,无论其长度和复杂程度如何,都可以使用蛮力猜测出来。设计强大的令牌生成机制的目的:在连续请求中重新标识用户身份的令牌,在其生成过程中,不应给攻击者提供任何机会,使其能够以常规方式预测或推断发布给其他用户的令牌,从而从应用程序中获得大量的令牌样本。即使攻击者拥有大量带宽和处理资源,他也绝无可能在令牌的有效期限内成功猜测出任何一个有效的令牌。
最有效的令牌生成机制应当使用数量极其庞大的一组可能值,并且包含强大的伪随机源,确保令牌以无法预测的方式平均分布在可能值范围内。
令牌中不应包含其他任何内容(除服务器用来定位处理用户请求的相关会话对象的一个标识符)。无论是公开显示还是隐藏在几层编码或模糊处理中,令牌都不应含有意义或采用结构。所有关于会话所有者与状态的数据都应保存在与会话令牌对应的服务器会话对象中。
选择最为稳定可靠的随机源。开发者应当认识到,各种可用的随机源在强度上可能存在巨大的差异。和java.util.Random一样,一些随机源非常适用于各种需要不断变化的输入源的情况,但只需根据唯一一个输出项就可以准确地推断出它的前后随机数。开发者应研究不同的可用随机源实际使用算法的数学特性,并阅读相关文档资料,了解API的推荐用法。一般来说,如果某种算法没有明确说明它具有加密安全性,那么应认为它可被预测。
除选择最为稳定可靠的随机源外,以与其生成令牌请求有关的一些信息作为熵源,也是一种良好的做法。这些信息可能并不是那个请求独有的,但能够非常有效地消除所使用的核心伪随机数发生器存在的任何缺陷。可被合并的信息包括请求时间(毫秒)、请求中的User-Agent消息头、来源IP地址(source IP address)及接收请求的端口号等元素。
合并这个熵的最有效公式是建立一个特殊的字符串,连接一个伪随机数、一串上面列出的与请求有关的数据以及一个仅服务器知道并在每次重启时重新生成的机密字符串。然后,使用适当的散列算法对这个字符串进行处理,生成一个固定长度、便于管理的字符串,并以它作为令牌。
决定生成会话令牌的算法后,一个有用的“思想试验”是想象伪随机源被完全攻破,并总是返回相同的随机数。如果出现这种情况,那么从应用程序中获得大量令牌样本的攻击者能够截获发布给其他用户的令牌吗?使用上面描述的公式,即使攻击者完全了解生成令牌所使用的算法,一般也绝无这种可能。来源IP、端口号、User-Agent消息头和请求时间共同生成一个数目庞大的熵。即使掌握所有这些信息,如果不知道服务器使用的机密字符串,攻击者仍然无法生成对应的令牌。
6.4.2 保障令牌使用过程中的安全
在令牌生成到废止的整个生命周期中保障它的安全,达到不会将其泄露给除令牌用户以外的其他任何人的目标要做到以下几方面。
(1)令牌只能通过HTTPS传送。
任何以明文形式传送的令牌都应视为被“污染”,也就是说,不能确保用户身份不被泄露。如果使用HTTP Cookies传送令牌,应将这些Cookies标记为安全,以防止用户浏览器通过HTTP传送它们。如果可能,应对每个应用程序页面使用HTTPS,包括静态内容(如帮助页面、图像等)。如果没有可能,并且仍然采用HTTP服务,那么应用程序应将任何访问敏感内容(包括登录页面)的请求重定向到HTTPS服务。帮助页面之类的静态资源一般不属于敏感内容,不需要使用通过验证的会话即可访问。因此,可以通过使用Cookies范围指令强化Cookies的使用安全,防止在访问这些资源的请求中提交令牌。
(2)绝不能在URL中传送会话令牌。
因为这样做易于受到会话固定攻击,并可能使令牌出现在各种日志机制中。有时候,开发者在禁用Cookies的浏览器中使用这种技巧执行会话。然而,最好是对所有导航使用POST请求实现这一目的,并将令牌保存在HTML表单隐藏字段中。
(3)应总是执行退出功能。
通过它删除服务器上的所有会话资源并终止会话令牌。
(4)处于非活动状态一段时间执行会话终止。
会话终止的效果应和用户完全退出的作用完全相同。
(5)防止并行登录。
每次一名用户登录,都应发布一个新会话令牌,同时废止任何属于该用户的现有会话,就好像他已经退出应用程序一样。如果旧令牌被保存一段时间,那么随后收到任何使用该令牌提出的请求,都应向用户发出安全警报,告诉他会话已被终止,因为他已经从其他位置登录。
(6)严密保护会话令牌的管理或诊断功能。
严密保护会话令牌的管理或诊断功能,以防止未授权的访问。许多时候,这种功能根本没有必要显示会话令牌,相反,它应提供足够的与会话所有者有关的信息,以便执行任何支持和诊断任务。这样做就不会泄露该用户提交的会话令牌,使攻击者劫持他的会话。
(7)尽可能限定应用程序会话Cookies的域和路径范围。
范围过于宽泛的Cookies通常是由配置不佳的Web应用程序平台或Web服务器生成的,而不是由应用程序开发者本人生成的。通过应用程序Cookies范围中的域名或URL路径,应无法访问其他Web应用程序或不可信的功能。应特别注意用于访问应用程序域名的任何现有子域。有时,为了确保不会造成这种漏洞,必须修改组织所使用的各种应用程序的域和路径命名方案。
(8)采取特殊措施保护会话管理机制的安全。
这可以防止应用程序用户成为各种攻击的目标。
(9)严格审查应用程序的代码库。
这可以确定并删除任何跨站点脚本漏洞。许多这类漏洞可被用于攻击会话管理机制,特别是保存型(或二阶)XSS攻击,它可对每一种会话滥用与劫持防御造成破坏。
(10)不应接受用户提交但服务器并不认可的任何令牌。
应立即在浏览器中取消该令牌,并将用户返回到应用程序的起始页面。
(11)进行两步确认或重新验证。
在执行转账之类的重要操作之前,可有效防御跨站点请求伪造和其他会话攻击。
(12)不完全依赖HTTP Cookies传送会话令牌。
这样可以防御跨站点请求伪造攻击。使用Cookies机制会造成这种漏洞是因为无论什么原因提出请求,浏览器都会自动提交Cookies。如果总是通过HTML表单隐藏字段传送令牌,那么除非攻击者已经知道令牌,否则他就无法建立一个表单,再通过提交该表单执行未授权操作。当然,如果他已经知道令牌,就可以轻易实施劫持攻击。每页面令牌也有助于防止这些攻击。
(13)成功验证后应总是建立一个新的会话。
可以避免会话固定攻击的影响。如果应用程序并不使用验证机制,但允许提交敏感数据,那么会话固定攻击造成的威胁就更难以解除。一种可能的解决办法是使提交敏感数据的页面序列尽可能短,并且在这个序列的第一个页面建立一个新的会话(如有必要,从现有会话中复制任何需要的数据,如购物车的内容),或者使用每页面令牌,防止知道第一个页面所使用的令牌攻击者访问随后的页面。除非完全有必要,否则不得向用户显示个人数据。即使有必要(如显示地址的“确认订单”页面),也不得向用户显示信用卡号码和密码之类的敏感数据,并且应在应用程序的响应中隐藏这些数据。
(14)应用每页面令牌。
应在会话令牌的基础上使用每页面令牌,对会话实施更加严格的控制,更有效地防御或阻断各种会话攻击。使用每页面令牌时,每次用户请求一个应用程序页面(而不是图像),应用程序都会建立一个新的页面令牌,并通过Cookies或HTML表单隐藏字段将其传送给客户。用户每次提出一个请求,除通过主会话令牌进行正常确认外,页面令牌还根据最后发布的令牌值进行再次检验。如果出现不匹配的情况,整个会话将被终止。
虽然使用每页面令牌确实给导航造成一些限制(如使用“后退”和“前进”按钮以及多窗口浏览方面),但它能够有效防御会话固定攻击,并确保如果合法用户和攻击者同时使用一个被劫持的会话提出相同的请求,该请求会立即被应用程序终止。每页面令牌还可用于追踪用户的位置和在应用程序中的活动情况,检测出不按预定顺序访问某些功能的企图,并有助于防止某些访问控制缺陷。
6.4.3 使用日志、监控等辅助功能
应用程序的会话管理功能应与它的日志、监控与警报机制紧密结合,以在适当时记录反常行为,并帮助管理员在必要时采取防御措施。应用程序应监控包含无效令牌的请求。除非令牌很容易被预测,否则,攻击者就需要提出大量包含无效令牌的请求,才能成功猜测出应用程序发布给其他用户的令牌,从而在应用程序的日志中留下明显的痕迹。
很难完全阻止针对会话令牌的蛮力攻击,因为我们无法通过禁用特殊用户账户或会话来终止这种攻击。一种可能的防御方法是在收到大量包含无效令牌的请求时将其来源IP地址屏蔽一段时间。然而,如果一个用户的请求来自几个IP地址(如AOL用户),或者几个用户的请求来自同一个IP地址(如执行网络地址转换的代理服务器或防火墙中的用户),这种方法就不能发挥太大的作用。
即使无法立即有效防止针对会话的蛮力攻击,但保留详细的日志并向管理员发出警报仍然可帮助他们对攻击进行调查,并尽其所能采取适当的行动。
只要有可能,应向用户警告与会话有关的反常事件,如并行登录或明显的劫持攻击(使用每页面令牌检测)。即使用户的令牌已被攻破,这样做也可促使用户进行检查,看是否发生转账之类的未授权操作。
那么什么又叫反应性会话终止呢?会话管理机制可非常有效地防御许多针对应用程序的其他攻击。如果收到用户提交的反常请求(例如,任何包含被修改的隐藏HTML表单字段或URL查询字符串参数的请求、任何包含与SQL注入或跨站点脚本攻击有关的字符串请求,以及任何正常情况下已经被长度限制之类的客户端检查阻止的用户输入),那么一些安全性至关重要的应用程序(如电子银行)会极其迅速地终止用户的会话。
当然,任何使用这类请求可对其加以利用的漏洞都必须从源头进行清除。但是,迫使用户每次提交一个无效请求时都需要进行重新验证会显著延长探查应用程序漏洞所需的时间,即使采用自动技巧完成这一任务也是如此。如果残余的漏洞确实存在,其他人就更不可能发现它们。
如果执行这种防御,为方便测试,建议在需要时将其关闭。如果在对应用程序进行合法渗透测试时,这种防御就像是遇到真正的攻击者那样减缓应用程序的响应速度,那么它的效率就会显著降低。与不使用这种机制相比,使用它很可能会在代码中造成更多的漏洞。