跳到内容

会话管理备忘录

简介

Web认证、会话管理和访问控制:

Web会话是与同一用户关联的一系列网络HTTP请求和响应事务。现代复杂的Web应用程序需要为每个用户在多个请求期间保留信息或状态。因此,会话提供了建立变量的能力——例如访问权限和本地化设置——这些变量将在会话期间应用于用户与Web应用程序的每次交互。

Web应用程序可以在用户第一次请求后创建会话以跟踪匿名用户。一个例子是维护用户语言偏好。此外,一旦用户完成身份验证,Web应用程序将利用会话。这确保了在任何后续请求中识别用户以及能够应用安全访问控制、授权访问用户私有数据和提高应用程序可用性的能力。因此,当前Web应用程序可以在认证前和认证后提供会话功能。

一旦建立认证会话,会话ID(或令牌)暂时等同于应用程序使用的最强认证方法,例如用户名和密码、密码短语、一次性密码(OTP)、基于客户端的数字证书、智能卡或生物识别(例如指纹或眼部视网膜)。参见OWASP 认证备忘录

HTTP是一种无状态协议(RFC2616第5节),其中每个请求和响应对都独立于其他Web交互。因此,为了引入会话概念,需要实现会话管理功能,将会话与Web应用程序中常见的认证和访问控制(或授权)模块关联起来。

SessionDiagram

会话ID或令牌将会话(以用户会话的形式)绑定到用户HTTP流量以及Web应用程序强制执行的适当访问控制。现代Web应用程序中这三个组件(认证、会话管理和访问控制)的复杂性,加上其实现和绑定都由Web开发人员决定(因为Web开发框架没有在这些模块之间提供严格的关系),使得实现安全的会话管理模块非常具有挑战性。

会话ID的泄露、捕获、预测、暴力破解或固定将导致会话劫持(或侧向劫持)攻击,攻击者能够完全模拟Web应用程序中的受害者用户。攻击者可以执行两种类型的会话劫持攻击:有针对性的或通用的。在有针对性的攻击中,攻击者的目标是模拟特定的(或特权的)Web应用程序受害者用户。对于通用攻击,攻击者的目标是模拟(或以)Web应用程序中的任何有效或合法用户身份访问。

会话ID属性

为了保持认证状态并跟踪用户在Web应用程序中的进度,应用程序会向用户提供一个在会话创建时分配的会话标识符(会话ID或令牌),该标识符在会话期间由用户和Web应用程序共享和交换(在每个HTTP请求中发送)。会话ID是一个name=value对。

为了实现安全的会话ID,标识符(ID或令牌)的生成必须满足以下属性。

会话ID名称指纹识别

会话ID使用的名称不应过于描述性,也不应提供有关ID目的和含义的不必要细节。

最常见的Web应用程序开发框架使用的会话ID名称可以很容易地被指纹识别,例如PHPSESSID(PHP)、JSESSIONID(J2EE)、CFIDCFTOKEN(ColdFusion)、ASP.NET_SessionId(ASP .NET)等。因此,会话ID名称可以泄露Web应用程序使用的技术和编程语言。

建议将Web开发框架的默认会话ID名称更改为通用名称,例如id

会话ID熵

会话标识符必须至少具有64位熵,以防止暴力破解会话猜测攻击。熵是指值中随机性或不可预测性的量。每个“位”的熵将可能结果的数量翻倍,这意味着具有64位熵的会话ID可以有2^64个可能的值。

必须使用强大的CSPRNG(加密安全伪随机数生成器)来生成会话ID。这确保了生成的值在所有可能值之间均匀分布。否则,攻击者可能能够使用统计分析技术来识别会话ID创建中的模式,从而有效降低熵,使攻击者更容易猜测或预测有效的会话ID。

注意:

  • 攻击者暴力破解有效会话ID的预期时间取决于熵位数、活动会话数量、会话过期时间以及攻击者的猜测速度等因素。
  • 如果一个Web应用程序生成具有64位熵的会话ID,假设攻击者每秒可以尝试10,000次猜测,并且应用程序中有100,000个有效的并发会话可用,攻击者预计需要大约585年才能成功猜测一个有效的会话ID。
  • 关于攻击者暴力破解会话标识符的预期时间的进一步分析可在此处找到。

会话ID长度

如前文会话ID熵部分所述,会话ID的一个主要安全要求是它们包含至少64位熵,以防止暴力猜测攻击。虽然会话ID长度很重要,但确保安全的是熵。会话ID必须足够长以编码足够的熵,从而防止攻击者猜测有效会话ID的暴力攻击。

不同的编码方法可以为相同的熵量产生不同的长度。会话ID通常使用十六进制编码表示。使用十六进制编码时,会话ID必须至少16个十六进制字符长,才能达到所需的64位熵。当使用不同的编码(例如Base64或Microsoft对ASP.NET会话ID的编码)时,可能需要不同数量的字符来表示最小的64位熵。

需要注意的是,如果会话ID的任何部分是固定的或可预测的,则有效熵会降低,并且可能需要增加长度以进行补偿。例如,如果一个16字符的十六进制会话ID的一半是固定的,则只有剩下的8个字符是随机的,提供仅32位熵——这对于强安全性来说是不够的。为了维护安全性,请确保整个会话ID是随机生成且不可预测的,或者如果ID的部分不是随机的,则增加总长度。

注意:

  • 有关会话ID长度与会话ID熵之间关系的更多信息,请参阅此处

会话ID内容(或值)

会话ID内容(或值)必须无意义,以防止信息泄露攻击,攻击者可以通过解码ID内容来提取用户、会话或Web应用程序内部工作方式的详细信息。

会话ID在客户端必须仅作为标识符,其值绝不能包含敏感信息或个人身份信息(PII)。要了解更多关于PII的信息,请参阅维基百科或这篇文章

与会话ID相关的含义、业务或应用程序逻辑必须存储在服务器端,具体来说,存储在会话对象中或会话管理数据库或存储库中。

存储的信息可以包括客户端IP地址、User-Agent、电子邮件、用户名、用户ID、角色、权限级别、访问权限、语言偏好、账户ID、当前状态、上次登录、会话超时和其他内部会话详细信息。如果会话对象和属性包含敏感信息,例如信用卡号,则需要对会话管理存储库进行适当加密和保护。

建议使用您的语言或框架创建的会话ID。如果您需要创建自己的会话ID,请使用至少128位大小的加密安全伪随机数生成器(CSPRNG),并确保每个会话ID都是唯一的。

会话管理实现

会话管理实现定义了用户和Web应用程序之间共享和持续交换会话ID的交换机制。HTTP中有多种机制可以维护Web应用程序中的会话状态,例如cookie(标准HTTP头)、URL参数(URL重写 – RFC2396)、GET请求中的URL参数、POST请求中的主体参数(例如隐藏表单字段(HTML表单))或专有HTTP头。

首选的会话ID交换机制应允许定义高级令牌属性,例如令牌过期日期和时间,或细粒度的使用限制。这是Cookie(RFC 210929656265)成为最广泛使用的会话ID交换机制之一的原因,它提供了其他方法中不具备的高级功能。

使用特定的会话ID交换机制,例如ID包含在URL中的机制,可能会泄露会话ID(在网络链接和日志、Web浏览器历史记录和书签、Referer头或搜索引擎中),并助长其他攻击,例如ID的操纵或会话固定攻击

内置会话管理实现

J2EE、ASP .NET、PHP等Web开发框架提供了各自的会话管理功能和相关实现。建议使用这些内置框架,而不是从头开始构建自己的框架,因为它们在全球多个Web环境中得到使用,并经过Web应用程序安全和开发社区的长期测试。

然而,请注意,这些框架在过去也曾出现漏洞和弱点,因此始终建议使用可用的最新版本,该版本可能修复了所有已知漏洞,并根据本文档中的建议审查和更改默认配置以增强其安全性。

会话管理机制用于临时保存会话ID的存储能力或存储库必须是安全的,保护会话ID免受本地或远程意外泄露或未经授权的访问。

已使用与接受的会话ID交换机制

Web应用程序应将会话ID交换管理使用Cookie。如果用户通过不同的交换机制(例如URL参数)提交会话ID,Web应用程序应避免将其接受作为阻止会话固定攻击的防御策略的一部分。

注意:

  • 即使Web应用程序使用Cookie作为其默认的会话ID交换机制,它也可能接受其他交换机制。
  • 因此,在处理和管理会话ID时,需要通过彻底测试确认Web应用程序当前接受的所有不同机制,并将接受的会话ID跟踪机制限制为仅Cookie。
  • 过去,一些Web应用程序使用URL参数,甚至在满足某些条件(例如,识别不支持Cookie或由于用户隐私问题而不接受Cookie的Web客户端)时,从Cookie切换到URL参数(通过自动URL重写)。

传输层安全

为了保护会话ID交换免受网络流量中的主动窃听和被动泄露,至关重要的是在整个Web会话期间使用加密的HTTPS (TLS) 连接,而不仅仅是在用户凭据交换的认证过程中。对于支持HTTP严格传输安全 (HSTS) 的客户端,这可以得到缓解。

此外,必须使用Secure cookie属性,以确保会话ID仅通过加密通道交换。使用加密通信通道还可以保护会话免受某些会话固定攻击,在这些攻击中,攻击者能够拦截和操纵Web流量以在受害者的Web浏览器上注入(或固定)会话ID。

以下一组最佳实践侧重于保护会话ID(特别是当使用Cookie时)并帮助在Web应用程序中集成HTTPS。

  • 不要将会话从HTTP切换到HTTPS,反之亦然,因为这会在网络中明文泄露会话ID。
    • 重定向到HTTPS时,确保在重定向发生设置或重新生成cookie。
  • 不要在同一页面或同一域名中混合加密和未加密的内容(HTML页面、图片、CSS、JavaScript文件等)。
  • 在可能的情况下,避免从同一主机提供公共未加密内容和私有加密内容。如果需要不安全内容,请考虑将其托管在单独的不安全域名上。
  • 实施HTTP 严格传输安全 (HSTS) 以强制使用HTTPS连接。

有关安全实施TLS的更多一般指导,请参阅OWASP 传输层安全备忘录

需要强调的是,TLS无法防御会话ID预测、暴力破解、客户端篡改或固定;然而,它确实提供了有效保护,防止攻击者通过中间人攻击拦截或窃取会话ID。

Cookie

基于Cookie的会话ID交换机制提供了多种安全功能,以Cookie属性的形式,可用于保护会话ID的交换。

Secure 属性

Secure Cookie属性指示Web浏览器仅通过加密的HTTPS (SSL/TLS) 连接发送Cookie。此会话保护机制是强制性的,以防止通过中间人(MitM)攻击泄露会话ID。它确保攻击者无法简单地从Web浏览器流量中捕获会话ID。

即使强制Web应用程序仅使用HTTPS进行通信(即使Web应用程序主机上的TCP/80端口HTTP已关闭),如果未设置Secure Cookie,也无法防止会话ID泄露——Web浏览器可能被欺骗,通过未加密的HTTP连接泄露会话ID。攻击者可以拦截并操纵受害者用户流量,并注入一个HTTP未加密的Web应用程序引用,这将强制Web浏览器以明文提交会话ID。

另请参阅:SecureFlag

HttpOnly 属性

HttpOnly Cookie属性指示Web浏览器不允许脚本(例如JavaScript或VBscript)通过DOM document.cookie对象访问Cookie。此会话ID保护是强制性的,以防止通过XSS攻击窃取会话ID。然而,如果XSS攻击与CSRF攻击结合,发送到Web应用程序的请求将包含会话Cookie,因为浏览器在发送请求时总是包含Cookie。HttpOnly Cookie仅保护Cookie的机密性;攻击者不能在XSS攻击上下文之外离线使用它。

参见OWASP XSS (跨站脚本) 防御备忘录

另请参阅:HttpOnly

SameSite 属性

SameSite 定义了一个 Cookie 属性,阻止浏览器在跨站请求中发送带有 SameSite 标记的 Cookie。主要目标是减轻跨域信息泄露的风险,并提供针对跨站请求伪造攻击的一些保护。

另请参阅:SameSite

Domain 和 Path 属性

Domain Cookie属性指示Web浏览器仅将Cookie发送到指定的域名及其所有子域名。如果未设置此属性,默认情况下Cookie将仅发送到源服务器。Path Cookie属性指示Web浏览器仅将Cookie发送到Web应用程序内指定的目录或子目录(或路径或资源)。如果未设置此属性,默认情况下Cookie将仅发送到请求并设置Cookie的资源所在目录(或路径)。

建议对这两个属性使用狭窄或受限的范围。因此,不应设置Domain属性(将cookie限制在源服务器),而Path属性应尽可能限制在使用会话ID的Web应用程序路径上。

Domain属性设置为过于宽松的值,例如example.com,允许攻击者对属于同一域名的不同主机和Web应用程序之间的会话ID发起攻击,这被称为跨子域cookie。例如,www.example.com中的漏洞可能允许攻击者通过secure.example.com获取会话ID。

此外,建议不要在同一域名上混合不同安全级别的Web应用程序。其中一个Web应用程序的漏洞将允许攻击者通过使用宽松的Domain属性(例如example.com)为同一域名上的不同Web应用程序设置会话ID,这是一种可用于会话固定攻击的技术。

尽管Path属性允许在同一主机上使用不同路径的不同Web应用程序之间隔离会话ID,但强烈建议不要在同一主机上运行不同的Web应用程序(特别是不同安全级别或范围的应用程序)。这些应用程序可以使用其他方法访问会话ID,例如document.cookie对象。此外,任何Web应用程序都可以为该主机上的任何路径设置Cookie。

Cookie容易受到DNS欺骗/劫持/中毒攻击,攻击者可以操纵DNS解析,强制Web浏览器泄露给定主机或域名的会话ID。

Expire 和 Max-Age 属性

基于Cookie的会话管理机制可以使用两种类型的Cookie:非持久(或会话)Cookie和持久Cookie。如果Cookie带有Max-Age(优先于Expires)或Expires属性,则它将被视为持久Cookie,并由Web浏览器存储在磁盘上直至过期时间。

通常,用于在认证后跟踪用户的会话管理功能使用非持久性cookie。这会强制在当前Web浏览器实例关闭时,会话从客户端消失。因此,强烈建议将会话管理目的使用非持久性cookie,以便会话ID不会长时间保留在Web客户端缓存中,从而可能被攻击者获取。

  • 通过确保敏感信息不持久化、对其进行加密并仅在需要时存储,来确保敏感信息不被泄露。
  • 确保未经授权的活动不会通过Cookie操作发生
  • 确保设置了安全标志,以防止在不安全的方式下意外通过网络传输
  • 确定应用程序代码中的所有状态转换是否正确检查了Cookie并强制使用它们
  • 如果敏感数据持久化在Cookie中,则应确保整个Cookie都已加密。
  • 定义应用程序使用的所有cookie,它们的名称以及为什么需要它们。

HTML5 Web Storage API

Web超文本应用技术工作组 (WHATWG) 将HTML5 Web Storage API,localStoragesessionStorage,描述为在客户端存储键值对的机制。与HTTP Cookie不同,localStoragesessionStorage的内容不会由浏览器在请求或响应中自动共享,它们用于在客户端存储数据。

localStorage API

作用域

使用localStorage API存储的数据可由从同一源加载的页面访问,同一源定义为方案(https://)、主机(example.com)、端口(443)和域/领域(example.com)。这为数据提供了与通过在Cookie上使用secure标志所能实现的类似访问,这意味着从https存储的数据无法通过http检索。由于可能存在来自独立窗口/线程的并发访问,使用localStorage存储的数据可能容易受到共享访问问题(例如竞态条件)的影响,并应被视为非锁定(Web Storage API 规范)。

持续时间

使用localStorage API存储的数据在不同的浏览会话中仍然存在,延长了其他系统用户可以访问它的时间范围。

离线访问

标准不要求localStorage数据在静止时加密,这意味着可以直接从磁盘访问这些数据。

用例

WHATWG 建议将localStorage用于需要在跨窗口或标签页、跨多个会话访问的数据,以及出于性能原因可能需要存储大量(数兆字节)数据的情况。

sessionStorage API

作用域

sessionStorage API将数据存储在其被调用的窗口上下文中,这意味着标签页1无法访问从标签页2存储的数据。此外,与localStorage API一样,使用sessionStorage API存储的数据可由从同一源加载的页面访问,同一源定义为方案(https://)、主机(example.com)、端口(443)和域/领域(example.com)。这为数据提供了与通过在Cookie上使用secure标志所能实现的类似访问,这意味着从https存储的数据无法通过http检索。

持续时间

sessionStorage API仅在当前浏览会话期间存储数据。一旦标签页关闭,数据将不再可检索。这并不一定阻止访问,如果浏览器标签页被重复使用或保持打开。数据也可能在内存中持续存在,直到发生垃圾回收事件。

离线访问

标准不要求sessionStorage数据在静止时加密,这意味着可以直接从磁盘访问这些数据。

用例

WHATWG建议将sessionStorage用于与单个工作流实例相关的数据,例如购票的详细信息,但可以在其他标签页中同时执行多个工作流。窗口/标签页绑定的性质将防止数据在不同标签页中的工作流之间泄漏。

参考资料

Web Workers

Web Workers 在与当前窗口不同的全局上下文中运行 JavaScript 代码。与主执行窗口之间存在一个通信通道,称为MessageChannel

用例

当页面刷新后不需要存储持久性时,Web Workers 是浏览器存储(会话)秘密的替代方案。为了让 Web Workers 提供安全的浏览器存储,任何需要秘密的代码都应该存在于 Web Worker 中,并且秘密绝不应该传输到主窗口上下文。

将秘密存储在Web Worker的内存中提供了与HttpOnly cookie相同的安全保障:秘密的机密性得到保护。尽管如此,XSS攻击仍可用于向Web Worker发送消息以执行需要秘密的操作。Web Worker会将操作结果返回给主执行线程。

与HttpOnly cookie相比,Web Worker实现的好处是Web Worker允许一些隔离的JavaScript代码访问秘密;HttpOnly cookie对任何JavaScript都不可访问。如果前端JavaScript代码需要访问秘密,Web Worker实现是唯一能够保留秘密机密性的浏览器存储选项。

会话ID生命周期

会话ID生成与验证:宽松和严格会话管理

Web应用程序的会话管理机制有两种类型,宽松型和严格型,它们与会话固定漏洞有关。宽松型机制允许Web应用程序最初接受用户设置的任何会话ID值作为有效值,并为其创建新会话,而严格型机制强制Web应用程序只接受Web应用程序先前生成的会话ID值。

会话令牌应尽可能由网络服务器处理,或通过加密安全随机数生成器生成。

尽管目前最常见的机制是严格机制(更安全),但PHP默认为宽松机制。开发人员必须确保Web应用程序在某些情况下不使用宽松机制。Web应用程序绝不应接受从未生成过的会话ID,如果收到此类ID,应生成并向用户提供一个新的有效会话ID。此外,这种情况应被检测为可疑活动并生成警报。

将会话ID作为任何其他用户输入进行管理

会话ID必须被视为不可信的,就像Web应用程序处理的任何其他用户输入一样,它们必须经过彻底的验证和校验。根据所使用的会话管理机制,会话ID将通过GET或POST参数、URL或HTTP头(例如cookie)接收。如果Web应用程序在处理无效会话ID值之前不进行验证和过滤,它们可能会被用于利用其他Web漏洞,例如如果会话ID存储在关系数据库中则可能导致SQL注入,或者如果会话ID被Web应用程序存储并随后反射回来则可能导致持久性XSS。

任何权限级别更改后更新会话ID

在关联用户会话中的任何权限级别更改后,Web应用程序必须更新或重新生成会话ID。会话ID重新生成强制执行的最常见场景是认证过程,因为用户权限级别从未经认证(或匿名)状态更改为认证状态,尽管在某些情况下可能仍未达到授权状态。常见的考虑场景包括:密码更改、权限更改,或在Web应用程序中从普通用户角色切换到管理员角色。对于Web应用程序的所有敏感页面,必须忽略任何以前的会话ID,每个对受保护资源的新请求都必须只分配当前的会话ID,并且旧的或以前的会话ID必须被销毁。

最常见的Web开发框架提供了会话功能和方法来更新会话ID,例如request.getSession(true)HttpSession.invalidate()(J2EE)、Session.Abandon()Response.Cookies.Add(new...)(ASP .NET),或session_start()session_regenerate_id(true)(PHP)。

会话ID重新生成是强制性的,以防止会话固定攻击,在这种攻击中,攻击者在受害者用户的Web浏览器上设置会话ID,而不是像其他大多数基于会话的攻击那样收集受害者的会话ID,并且与使用HTTP或HTTPS无关。这种保护措施减轻了其他基于Web的漏洞的影响,这些漏洞也可以用于发起会话固定攻击,例如HTTP响应拆分或XSS(请参见此处此处)。

一项补充建议是在认证前和认证后使用不同的会话ID或令牌名称(或一组会话ID),以便Web应用程序可以跟踪匿名用户和认证用户,而不会有暴露或绑定两个状态之间用户会话的风险。

使用多个Cookie时的注意事项

如果Web应用程序使用Cookie作为会话ID交换机制,并且为给定会话设置了多个Cookie,则Web应用程序必须在允许访问用户会话之前验证所有Cookie(并强制它们之间的关系)。

Web应用程序在HTTP上预认证时设置用户Cookie以跟踪未经认证(或匿名)用户非常常见。一旦用户在Web应用程序中认证,一个新的后认证安全Cookie会通过HTTPS设置,并且在两个Cookie和用户会话之间建立绑定。如果Web应用程序不验证认证会话的两个Cookie,攻击者可以利用预认证的非保护Cookie来访问认证用户会话(参见此处此处)。

Web应用程序应尽量避免在同一Web应用程序中使用相同的Cookie名称用于不同的路径或域范围,因为这会增加解决方案的复杂性,并可能引入作用域问题。

会话过期

为了最大限度地缩短攻击者可以对活动会话发起攻击并劫持它们的时间,强制为每个会话设置过期超时是必要的,以确定会话将保持活动状态的时间。Web应用程序的会话过期时间不足会增加其他基于会话的攻击的暴露风险,因为攻击者要重用有效的会话ID并劫持关联的会话,它必须仍然处于活动状态。

会话间隔越短,攻击者利用有效会话ID的时间就越少。会话过期超时值必须根据Web应用程序的目的和性质进行设置,并平衡安全性和可用性,以便用户可以舒适地完成Web应用程序中的操作,而不会会话频繁过期。

空闲超时和绝对超时值都高度依赖于Web应用程序及其数据的关键程度。对于高价值应用程序,常见的空闲超时范围是2-5分钟,对于低风险应用程序是15-30分钟。绝对超时取决于用户通常使用应用程序的时间。如果应用程序旨在供办公室工作人员全天使用,则适当的绝对超时范围可能在4到8小时之间。

当会话过期时,Web应用程序必须采取主动行动,在客户端和服务器两端使会话失效。从安全角度来看,后者是最相关且强制性的。

对于大多数会话交换机制,客户端使会话ID失效的操作基于清除令牌值。例如,要使Cookie失效,建议将会话ID设置为空(或无效)值,并将Expires(或Max-Age)属性设置为过去的日期(如果使用的是持久性Cookie):Set-Cookie: id=; Expires=Friday, 17-May-03 18:45:00 GMT

为了在服务器端关闭并使会话失效,当会话过期或用户主动注销时,Web应用程序必须通过使用会话管理机制提供的功能和方法(例如HttpSession.invalidate() (J2EE)、Session.Abandon() (ASP .NET) 或session_destroy()/unset() (PHP))来采取主动行动。

自动会话过期

空闲超时

所有会话都应实现空闲或非活动超时。此超时定义了在会话中没有活动的情况下,会话将保持活动状态的时间量,在Web应用程序针对给定会话ID收到最后一个HTTP请求后,在定义的空闲期过后关闭并使会话失效。

空闲超时限制了攻击者猜测和使用其他用户有效会话ID的机会。然而,如果攻击者能够劫持给定会话,空闲超时并不会限制攻击者的行为,因为他们可以定期在会话上生成活动,以使会话保持活动更长时间。

会话超时管理和过期必须在服务器端强制执行。如果客户端用于强制会话超时,例如使用会话令牌或其他客户端参数来跟踪时间参考(例如自登录时间起的分钟数),攻击者可以操纵这些参数来延长会话持续时间。

绝对超时

所有会话都应该实现一个绝对超时,无论会话活动如何。这个超时定义了一个会话可以活动的最大时间量,在Web应用程序最初创建该会话以来的定义绝对时间段后关闭并使会话失效。会话失效后,用户将被强制重新认证并建立一个新的会话。

绝对会话限制了攻击者使用被劫持会话并冒充受害者用户的时间。

更新超时

或者,Web应用程序可以实现一个额外的更新超时,在该超时之后,会话ID会在用户会话中间自动更新,并且独立于会话活动和空闲超时。

在会话最初创建经过特定时间后,Web应用程序可以为用户会话重新生成一个新的ID并尝试在客户端上设置或更新它。之前的会话ID值将在一段时间内仍然有效,以提供一个安全间隔,直到客户端意识到新的ID并开始使用它。届时,当客户端在当前会话中切换到新的ID时,应用程序将使旧的ID失效。

这种情况最大限度地减少了给定会话ID值(可能被攻击者获取)被重用来劫持用户会话的时间,即使受害者用户会话仍然处于活动状态。用户会话在合法客户端上保持活动和打开状态,尽管其关联的会话ID值在会话期间每次更新超时过期时都会透明地更新。因此,更新超时补充了空闲和绝对超时,特别是当绝对超时值随着时间的推移显著延长时(例如,应用程序要求用户会话长时间保持打开状态)。

根据实现情况,可能会出现竞态条件,即持有仍然有效的旧会话ID的攻击者在受害者用户之前发送请求,紧接着更新超时刚刚过期,并首先获得更新后的会话ID值。至少在这种情况下,受害者用户可能会意识到攻击,因为会话将突然终止,因为关联的会话ID不再有效。

手动会话过期

Web应用程序应提供允许具有安全意识的用户在使用完Web应用程序后主动关闭其会话的机制。

注销按钮

Web应用程序必须提供一个可见且易于访问的注销(退出、登出或关闭会话)按钮,该按钮应在Web应用程序的头部或菜单中可用,并可从每个Web应用程序资源和页面访问,以便用户可以随时手动关闭会话。如“会话过期”部分所述,Web应用程序必须至少在服务器端使会话失效。

注意:不幸的是,并非所有Web应用程序都方便用户关闭其当前会话。因此,客户端增强功能允许有良知的用户通过帮助他们勤奋地关闭会话来保护其会话。

Web内容缓存

即使会话结束后,会话期间交换的私人或敏感数据仍可能通过Web浏览器缓存进行访问。为缓解这种情况,Web应用程序必须对所有HTTP和HTTPS流量使用限制性缓存指令。这包括在所有页面(尤其是显示敏感内容的页面)中使用HTTP头,例如Cache-ControlPragma,或等效的<meta>标签。

会话标识符绝不能被缓存。为了防止这种情况,强烈建议在包含会话ID的响应中包含Cache-Control: no-store指令。与no-cache允许缓存但需要重新验证不同,no-store确保响应(包括Set-Cookie等头)绝不会存储在任何缓存中。

注意:有时建议使用指令Cache-Control: no-cache="Set-Cookie, Set-Cookie2"来防止会话ID缓存。然而,这种语法并未得到广泛支持,并可能导致意外行为。相反,使用Cache-Control: no-store以获得更强的保护。参考:MDN - Cache-Control

风险事件后的重新认证

为了确保会话完整性和账户保护,应用程序应在检测到特定高风险事件时要求重新认证。这可能包括:

  • 尝试或完成密码更改
  • 从新的或可疑的IP地址或设备登录
  • 完成账户恢复或挑战流程(例如,黑客锁定场景)

要求重新认证有助于缓解会话劫持和未经授权的访问——尤其是在使用长期会话或外部身份提供者时。

推荐实践

  • 提示用户输入主要凭据(例如密码)或强制执行MFA
  • 提供清晰的消息说明重新认证的必要性

会话管理的其他客户端防御

Web应用程序可以配合前面描述的会话管理防御措施,增加客户端的其他对策。客户端保护,通常以JavaScript检查和验证的形式出现,并非万无一失,熟练的攻击者可以轻易绕过,但它们可以引入另一层防御,入侵者必须绕过。

初始登录超时

Web应用程序可以在登录页面使用JavaScript代码来评估和测量自页面加载并授予会话ID以来所花费的时间。如果在特定时间后尝试登录,客户端代码可以通知用户已超过最大登录时间,并重新加载登录页面,从而检索新的会话ID。

这种额外的保护机制试图强制在认证前更新会话ID,避免了在会话固定攻击中,先前使用过(或手动设置)的会话ID被使用同一台计算机的下一个受害者重复利用的场景。

在Web浏览器窗口关闭事件时强制会话注销

Web应用程序可以使用JavaScript代码捕获所有Web浏览器标签页或窗口关闭(甚至返回)事件,并在关闭Web浏览器之前采取适当的措施来关闭当前会话,模拟用户已通过注销按钮手动关闭会话。

禁用Web浏览器跨标签页会话

一旦用户登录并建立会话,Web应用程序就可以使用JavaScript代码,如果针对同一Web应用程序打开新的Web浏览器标签页或窗口,则强制用户重新认证。Web应用程序不希望允许多个Web浏览器标签页或窗口共享同一会话。因此,应用程序会尝试强制Web浏览器不同时共享同一会话ID。

注意:如果会话ID通过Cookie交换,则无法实现此机制,因为Cookie由所有Web浏览器标签页/窗口共享。

自动客户端注销

Web应用程序可以在所有(或关键)页面中使用JavaScript代码,在空闲超时过期后自动注销客户端会话,例如,通过将用户重定向到注销页面(与之前提到的注销按钮使用的资源相同)。

通过客户端代码增强服务器端空闲超时功能的好处是,用户可以看到会话由于不活动而结束,甚至可以通过倒计时器和警告消息提前收到会话即将过期的通知。这种用户友好的方法有助于避免因服务器端静默过期的会话而导致需要大量输入数据的网页中工作丢失。

会话攻击检测

会话ID猜测和暴力破解检测

如果攻击者尝试猜测或暴力破解有效会话ID,他们需要从一个(或一组)IP地址向目标Web应用程序发起多个连续请求,使用不同的会话ID。此外,如果攻击者尝试分析会话ID的可预测性(例如使用统计分析),他们需要从一个(或一组)IP地址向目标Web应用程序发起多个连续请求,以收集新的有效会话ID。

Web应用程序必须能够根据尝试收集(或使用)不同会话ID的次数来检测这两种情况,并向违规IP地址发出警报和/或阻止。

检测会话ID异常

Web应用程序应着重检测与会话ID相关的异常,例如其操纵。OWASP AppSensor项目提供了一个框架和方法,用于在Web应用程序内部实现内置入侵检测功能,重点检测异常和意外行为,以检测点和响应动作的形式。与使用外部保护层不同,有时业务逻辑细节和高级智能只能从Web应用程序内部获取,在那里可以建立多个与会话相关的检测点,例如当现有Cookie被修改或删除时,添加新Cookie时,重用另一个用户的会话ID时,或者会话期间用户位置或User-Agent发生变化时。

将会话ID绑定到其他用户属性

为了检测(并在某些情况下保护)用户不当行为和会话劫持,强烈建议将会话ID绑定到其他用户或客户端属性,例如客户端IP地址、User-Agent或基于客户端的数字证书。如果Web应用程序在已建立会话期间检测到这些不同属性之间的任何更改或异常,这是一个非常好的会话操纵和劫持尝试的指示,这个简单的事实可以用于警报和/或终止可疑会话。

尽管这些属性不能被Web应用程序用来可靠地防御会话攻击,但它们显著增强了Web应用程序的检测(和保护)能力。然而,熟练的攻击者可以通过重用分配给受害者用户的相同IP地址(在NAT环境,如Wi-Fi热点中非常常见)或使用相同的出站Web代理(在企业环境中非常常见),或者通过手动修改User-Agent使其看起来与受害者用户的User-Agent完全相同来绕过这些控制。

记录会话生命周期:监控会话ID的创建、使用和销毁

Web应用程序应通过包含会话完整生命周期的信息来增强其日志记录功能。特别是,建议记录与会话相关的事件,例如会话ID的创建、更新和销毁,以及其在登录和注销操作中的使用详情、会话中的权限级别更改、超时过期、无效会话活动(如果检测到)以及会话期间的关键业务操作。

日志详细信息可能包括时间戳、源IP地址、请求的Web目标资源(和涉及会话操作)、HTTP头(包括User-Agent和Referer)、GET和POST参数、错误代码和消息、用户名(或用户ID),以及会话ID(Cookie、URL、GET、POST…)。

敏感数据如会话ID不应包含在日志中,以保护会话日志免受会话ID本地或远程泄露或未经授权的访问。然而,必须记录某种会话特定信息,以便将会话日志条目与特定会话关联。建议记录会话ID的加盐哈希值,而不是会话ID本身,以便在不暴露会话ID的情况下进行会话特定日志关联。

特别是,Web应用程序必须彻底保护允许管理所有当前活动会话的管理界面。这些界面经常被支持人员用来解决会话相关问题,甚至是一般问题,通过模拟用户并以用户的方式查看Web应用程序。

会话日志成为Web应用程序主要入侵检测数据源之一,也可被入侵保护系统用于在检测到攻击时自动终止会话和/或禁用用户账户。如果实施了主动保护措施,则也必须记录这些防御行为。

同时会话登录

Web应用程序设计决定是否允许同一用户从相同或不同客户端IP地址进行多个同时登录。如果Web应用程序不希望允许多个同时会话登录,则必须在每次新的认证事件后采取有效措施,隐式终止之前可用的会话,或询问用户(通过旧会话、新会话或两者)哪个会话必须保持活动。

建议Web应用程序添加用户功能,允许随时检查活动会话的详细信息,监控并提醒用户并发登录,提供用户功能以远程手动终止会话,并通过记录多个客户端详细信息(如IP地址、User-Agent、登录日期和时间、空闲时间等)来跟踪账户活动历史(日志)。

会话管理WAF保护

在某些情况下,Web应用程序源代码不可用或无法修改,或者为实现上述多项安全建议和最佳实践所需的更改意味着Web应用程序架构的全面重新设计,因此在短期内难以轻易实现。

在这种情况下,或为了补充Web应用程序的防御,并为了尽可能地保护Web应用程序的安全,建议使用外部保护措施,例如Web应用程序防火墙(WAF),它们可以缓解已描述的会话管理威胁。

Web应用程序防火墙提供针对基于会话攻击的检测和保护功能。一方面,WAF可以轻易强制在Cookie上使用安全属性,例如SecureHttpOnly标志,通过对所有设置新Cookie的Web应用程序响应的Set-Cookie头部应用基本重写规则。

另一方面,可以实现更高级的功能,允许WAF跟踪会话及相应的会话ID,并应用各种保护措施,以防止会话固定(在检测到权限更改时在客户端重新生成会话ID),强制粘性会话(通过验证会话ID与其他客户端属性(如IP地址或User-Agent)之间的关系),或管理会话过期(通过强制客户端和Web应用程序终止会话)。

开源的ModSecurity WAF,加上OWASP 核心规则集,提供了检测和应用安全Cookie属性、对抗会话固定攻击的对策以及强制粘性会话的会话跟踪功能。