OWASP 关于会话管理

偶尔善良 提交于 2021-01-09 07:00:53

英文原文:https://github.com/OWASP/CheatSheetSeries/blob/master/cheatsheets/Session_Management_Cheat_Sheet.md
采集日期:2019-07-17
注:本文 Session 与 “会话” 通用。

简介(Introduction)

Web 身份认证、Session 管理和访问控制:(Web Authentication, Session Management, and Access Control)

所谓 Web 会话(Session),就是与同一用户相关联的一连串网络 HTTP 请求和响应。当今较为复杂的 Web 应用程序,都需要在多次请求期间为每个用户维持信息或状态数据。为此,Session 提供了建立变量的能力 - 比如访问权限和本地化设置 - 用户在 Session 存续期内每次与 Web 应用程序的交互都能使用这些变量。

在首次用户请求之后,Web 应用程序即可创建 Session 对匿名用户进行追踪记录。比如可以记录用户的语言首选项。此外,只要用户完成了身份认证(Authentication),Web 应用程序就要用到 Session 了。这样在后续所有请求中,就确保能识别出用户,也能进行安全访问控制,也能对用户私有数据进行授权访问,以及增加应用程序的适用性。因此,在身份验证之前和之后,当今的 Web 应用程序都能提供 Session 的支持能力。

已通过身份认证的 Session 一经建立,Session ID(或 Token)就临时等同于应用程序最坚固的身份认证方法,正如用户名和密码、密码短语(Passphrase)、一次性密码(OTP)、基于客户端的数字证书、智能卡或生物识别技术(比如指纹或视网膜)。 参见 OWASP 的 Authentication Cheat Sheet

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

Session 示意图

Session ID 或 Token 将用户身份认证凭据(以用户 Session 的形式)绑定到用户的 HTTP 传输过程和 Web 应用程序执行的访问控制。当今 Web 应用程序中的这3个组件(身份认证、Session 管理和访问控制)相当复杂,而且事实上其实现和绑定过程都得靠 Web 开发人员自己来完成(因为 Web 开发框架并未对这些模块之间的严格关联提供支持),这就让实现安全的 Session 管理模块变得极具挑战性。

Session ID 的泄露、捕获、预测、暴力破解或固定(Fixation),都将导致 Session 劫持(Hijack 或 Sidejack)攻击,这时攻击者能够完全模拟 Web 应用程序中的受害用户。攻击者可以执行两类 Session 劫持攻击,目标攻击或泛攻击。在目标攻击中,攻击者的目标是假冒特定(或拥有特权)的 Web 应用程序受害用户。而在泛攻击时,攻击者的目标是假冒(或获取访问权限)Web 应用程序中的任何有效或合法用户。

Session ID 的特性(Properties)

为了维持用户认证状态,并记录用户在 Web 应用程序中的位置,应用程序会向用户提供一个在创建 Session 时分配的 Session 标识符(Session ID 或 Token)。在 Session 存续期间,用户和 Web 应用程序将共同持有并相互交换(Exchange)此标识符(每次 HTTP 请求都会发送)。Session ID 是一个键值对(name = value pair)。

为了实现安全的 Session ID,标识符(ID 或 Token)的生成必须满足以下特性。

Session ID 名称的识别(Fingerprint)

Session ID 使用的名称不应过于描述性,也不应给出与 ID 的用途和含义相关的无谓细节。

对于那些最常见的 Web 应用程序开发框架,其采用的 Session ID 名称很容易识别出来,比如 PHPSESSID (PHP), JSESSIONID (J2EE), CFID & CFTOKEN (ColdFusion), ASP.NET_SessionId(ASP .NET)等。因此,Session ID 的名称可以揭示出 Web 应用程序采用的技术和编程语言。

建议将 Web 开发框架默认的 Session ID 名称更改为id之类的通用名。

Session ID 的长度

攻击者可能会遍历 Session ID 的整个取值范围,并验证是否存在合法的 Session 。为了防范这种暴力攻击,Session ID 必须拥有足够的长度。

Session ID 的长度必须至少为128位(16字节)

注意

  • 根据下一节Session ID 的熵值中给出的假设,这里给出128位的 Session ID 长度以供参考。但该长度不应被视为绝对不变的最小值,因为实现过程中的其他因素可能会影响其强度。
  • 例如,有些知名的实现方案是这样的,正如 Microsoft ASP.NET session ID 所述:“*ASP.NET 的 Session 标识符是一个随机生成的数字,并编码为24个字符长度的字符串,由 a 到 z 的小写字母和 0 到 5 的数字组成”。
  • 这个长度能够提供很好的有效熵,因此可被视为足以躲避猜测或暴力攻击。

Session ID 的熵值(Entropy)

为了防范上述猜测攻击,Session ID 必须是不可预测的(足够随机)。在猜测攻击时,攻击者能够通过统计分析技术,猜测或预测合法 Session 的 ID。因此,必须采用优质的 PRNG(伪随机数生成器,Pseudo Random Number Generator)。

Session ID 值必须至少能支持64位的熵值。如果采用了优质的 PRNG,则该熵值差不多是 Session ID 长度的一半。

注意

  • 其实,Session ID 的熵值会受到其他外部因素和难以评估因素的影响,比如 Web 应用程序日常并发活动 Session 数、Session 绝对过期时间(Absolute session expiration timeout)、攻击者每秒可生成猜测的及目标 Web 应用程序可支持的 Session ID 数量等。

  • 如果采用熵值为64位的 Session ID,则1个攻击者至少要耗费292年才能成功猜出1个合法的 Session ID。这里假设攻击者每秒可以尝试10,000次猜测,并且 Web 应用程序中同时有100,000个 Session 可用。

  • 更多信息请看这里

Session ID 的内容(或值)

为了防止信息泄露攻击,Session ID 的内容(或值)必须没有什么含义。在信息泄露攻击时,攻击者能够解码 ID 的内容,并提取出用户、 Session 或 Web 应用程序内部工作过程的细节。

Session ID 必须只是客户端的标识符而已,其值绝不能包含敏感信息(或 PII)。

与 Session ID 关联的含义、业务或应用程序逻辑,都必须保存在服务器端,具体来说就是存储在 Session 对象、 Session 管理数据库(Database 或 Repository)中。

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

建议采用 SHA256 之类的加密哈希函数来创建密码强度较高的 Session ID。

Session 管理功能的实现(Session Management Implementations)

Session 管理功能定义了用户和 Web 应用程序之间将会采用的交换机制,以便共同持有并持续交换 Session ID。在 HTTP 协议中有多种机制可用于维护 Web 应用程序的 Session 状态,例如 cookie(标准 HTTP 头)、URL参数(URL重写 - RFC2396)、基于 GET 请求的 URL 参数、基于 POST 请求的 body 参数(比如隐藏的 HTML 表单字段)或专门的 HTTP 头。

建议 Session ID 交换机制应该允许定义高级的 Token 属性,例如 Token 过期日期和时间、粒度约束。Cookie(210929656265)能成为应用最广的 Session ID 交换机制之一,原因之一就是 Cookie 能提供其他方法所不具备的高级功能。

某些特定 Session ID 交换机制的采用,比如在 URL 中包含 ID,可能会泄露 Session ID(在 Web 链接和日志、Web 浏览器历史记录和书签、Referer 头部信息或搜索引擎中),并且还会为其他类型的攻击提供便利,如操纵 ID 或会话固定攻击(Session Fixation Attack)

内置 Session 管理功能的实现(Built-in Session Management Implementations)

Web 开发框架(如J2EE,ASP .NET,PHP等)提供了自己的 Session 管理功能和相关的实现方案。推荐采用这些内置框架,而不是从头开始构建自制的框架,因为这些内置框架已在全球范围内应用于多种 Web 环境,并且已经过 Web 应用程序安全和开发社区的测试。

不过,请注意这些框架曾经也存在过漏洞和弱点,因此始终建议大家采用最新的可用版本,新版系统有可能会修复所有知名漏洞,并按照本文所述的建议检查及修改默认配置,以便增强其安全性。

Session 管理机制用于暂存 Session ID 的存储或数据库必须安全可靠,以保护 Session ID 免遭本地或远程的意外泄露及未经授权的访问。

已在用和已采纳的 Session ID 交换机制(Used vs. Accepted Session ID Exchange Mechanisms)

Web 应用程序应该利用 Cookie 进行 Session ID 交换管理。如果用户通过其他交换机制(例如 URL 参数)提交 Session ID,则 Web 应用程序应该不予接受,这算是阻止会话固定攻击的防御策略之一。

注意

  • 即便是采用 Cookie 作为默认的 Session ID 交换机制,Web 应用程序仍然可能能够接受别的交换机制。
  • 因此,在处理和管理 Session ID 时,需要通过全面测试来确认 Web 应用程序当前可接受的所有交换机制,并将可接受的 Session ID 记录机制限制为仅支持 Cookie。
  • 以前有一些 Web 应用程序曾经采用 URL 参数作为交换机制,或者在满足某些条件时甚至可从 Cookie 切换成 URL 参数(通过 URL 自动重写技术),这些条件诸如 Web 客户端确认不支持 Cookie,或者因考虑用户隐私而不接受 Cookie。

传输层的安全性(Transport Layer Security)

为了保护 Session ID 交换过程在网络传输时免遭主动窃听(Active Eavesdrop)和被动泄露,必须强制对整个 Web 会话采用加密 HTTPS(SSL/TLS)连接,而不仅只是对交换用户凭据时的身份认证过程才使用 HTTPS。

此外还必须采用 Cookie 的 Secure 属性来确保仅通过加密通道交换 Session ID。使用加密通信通道还能保护 Session 免受某些会话固定攻击,这时的攻击者能够拦截并操纵网络传输过程,以便在受害者的 Web 浏览器上注入(或修正)Session ID,参见此处此处

以下是一些有关 HTTPS(SSL/TLS)的最佳实践,重点关注对 Session ID 的保护(特别是在使用 Cookie 时),以及在 Web 应用程序中集成 HTTPS:

  • Web 应用程序绝不能把已存在的 Session 从 HTTP 切换到 HTTPS,反之亦然,因为这将在网络上以明文形式泄露 Session ID。

  • Web 应用程序不应在同一主机(甚至是域,请参阅 Cookie 的 Domain 属性)上混合使用加密和未加密的内容(HTML 页面、图像、CSS、Javascript 文件等),因为未加密通道上对任何 Web 对象的请求都有可能泄露 Session ID。

  • 通常 Web 应用程序不应由同一主机同时提供公开的未加密内容和私密的加密内容。建议改为使用两台不同的主机,比如公开内容通过 HTTP(未加密)的 www.example.com,而私密和敏感内容(存在 Session)则通过 HTTPS(加密)的 secure.example.com。前者只开放 TCP/80 端口,而后者只开放 TCP/443 端口。

  • Web 应用程序应该避免主页从 HTTP 到 HTTPS 的重定向跳转(用 30x HTTP 响应),这种重定向是相当常见的做法。因为这一个未受保护的 HTTP 请求/响应交换过程会被攻击者用于收集(或修正)合法的 Session ID。

  • Web 应用程序应该利用 HTTP Strict Transport Security (HSTS)(曾被称作 STS)以强制使用 HTTPS 连接。

参见 OWASP Transport Layer Protection Cheat Sheet

有一个重点需要强调一下,SSL/TLS(HTTPS)无法防范 Session ID 预测、暴力破解、客户端篡改或 Session 固定攻击。不过到目前为止,从网络传输过程中泄露和捕获 Session ID,仍然是最普遍的攻击途径(Vector)之一。

Cookie

基于 Cookie 的 Session ID 交换机制提供了多种安全特性,可用于保护 Session ID 的交换过程。这些特性以 Cookie 属性的形式提供:

Secure 属性

Cookie 的 Secure 属性通知 Web 浏览器只能通过加密的 HTTPS(SSL/TLS)连接发送 Cookie。该会话保护机制可以确保防止 Session ID 经由 MitM(中间人)攻击而泄露 。它确保攻击者无法从 Web 浏览器传输过程中轻易捕获 Session ID。

即便强制 Web 应用程序仅采用 HTTPS 协议进行通信,甚至 Web 应用程序所在主机关闭了 HTTP 的 TCP/80 端口,但如果未设置 Cookie 的 Secure 属性,那么仍然无法防止 Session ID 的泄露。因为 Web 浏览器仍可以被蒙在鼓里,通过不加密的 HTTP 连接泄露出 Session ID。攻击者可以拦截和操纵受害用户的网络传输过程,并向 Web 应用程序注入未加密的 HTTP 引用,此引用将强制 Web 浏览器以明文形式提交 Session ID。

参见 Secure 标志

HttpOnly 属性

Cookie 的 HttpOnly 属性通知 Web 浏览器不允许脚本(比如 JavaScript 或 VBscript)通过 DOM document.cookie 对象访问 Cookie。该条 Session ID 保护措施可以确保无法通过 XSS 攻击窃取 Session ID。

参见 OWASP XSS (Cross Site Scripting) Prevention Cheat Sheet

参见 HttpOnly

SameSite 属性

服务器可以定义 Cookie 的 SameSite 属性,使得浏览器无法对跨站(Cross-site)请求发送该 Cookie。其主要目标是降低跨域(Cross-origin)信息泄露的风险,并为抵御跨站请求伪造攻击提供一些保护。

参见 SameSite

Domain 和 Path 属性

Cookie 的 Domain 属性 通知 Web 浏览器,只向指定的域及其全部子域发送该 Cookie。如果未设置 Domain 属性,则该 Cookie 默认只会发送给源服务器。Cookie 的 Path 属性通知 Web 浏览器,只向 Web 应用程序内的指定目录或子目录(或路径、资源)发送该 Cookie。如果未设置 Path 属性,则默认只会为被请求且设置了该 Cookie 的资源所在目录(或路径)发送该 Cookie。

建议缩小或限制这两个属性的使用范围。这样,就不应设置 Domain 属性(将Cookie 限定于源服务器),并且应将 Path 属性尽可能限定为需要用到 Session ID 的 Web 应用程序路径。

如果 Domain 属性设置得过于宽松,比如 example.com,攻击者就能用此 Session ID 对同一域中的其他主机和 Web 应用程序发起攻击,这被称为跨子域 Cookie。例如,www.example.com 中的漏洞或许能让攻击者从secure.example.com 中获取到 Session ID。

此外,建议不要在同一域中混合部署不同安全级别的 Web 应用程序。某一个 Web 应用程序中的漏洞,或许能让攻击者利用宽松的 Domain属性(如 example.com),为同一域内的其他 Web 应用程序设置 Session ID,这是一种在会话固定攻击时可被采用的技术。

尽管 Path 属性允许用同一主机上的不同路径隔离不同 Web 应用程序的 Session ID,但仍然强烈建议不要在同一主机上运行不同的 Web 应用程序(尤其是不同的安全级别或范围)。这些应用程序可以用其他方法来访问 Session ID,比如 document.cookie 对象。并且任何 Web 应用程序都可以为该主机中的任何路径设置 Cookie。

Cookie 很容易受到 DNS 欺骗/劫持/中毒攻击,这时攻击者可以操纵 DNS 解析系统来强制 Web 浏览器公开给定主机或域的 Session ID。

Expire 和 Max-Age 属性

基于 Cookie 的 Session 管理机制可以采用两种类型的 Cookie:非持久性(或 Session)Cookie 和持久性 Cookie。如果 Cookie 给出了 Max-Age(优先于 Expires)或 Expires 属性,则其会被视作一个持久性 Cookie,并将由 Web 浏览器存入磁盘至过期为止。

通常,如果在身份认证通过后, Session 管理功能需要记录用户状态信息,则可利用非持久性 Cookie。在当前 Web 浏览器实例关闭后,这会强制让 Session 从客户端消失。因此,如果想要进行 Session 管理,则强烈建议采用非持久性 Cookie,使得 Session ID 不会在 Web 客户端缓存中长期留存,而攻击者正是从缓存中获取到它的。

  • 确保使用敏感信息时应按需进行非持久化/加密/存储,以确保 Cookie 中不包含敏感信息。
  • 确保无法经由 Cookie 操作进行未经授权的活动。
  • 确保已设置了 Secure 标志,以防止经由网络用非安全方式进行意外传输。
  • 确定应用程序代码中的所有状态转换是否都对 Cookie 做了正确检查并强制启用。
  • 如果 Cookie 中存有敏感数据,请确保对整个 Cookie 进行加密。
  • 应用程序用到的所有 Cookie 都有明确的定义,具备名称和使用理由。

HTML5 Web 存储 API

Web 超文本应用技术工作组(WHATWG,Web Hypertext Application Technology Working Group)给出了 HTML5 Web 存储 API 的说明,包括 localStoragesessionStorage,以此作为客户端存储键值对(Name-value)的机制。

与 HTTP Cookie 不同,localStoragesessionStorage 中的内容在浏览器的请求或响应中不会自动分享,而是用于在客户端存储数据。

localStorage API

作用域(Scope)

所有由同一服务源加载的页面,都可以访问用 localStorage API 存储的数据。该服务源以 Scheme(https:\\)、主机(example.com)、端口(443) 和域(domain/realm)(example.com)的形式定义。

这种数据访问方式与带 Secure 标志的 Cookie 类似,意味着无法通过 http 协议读取经由 https 协议存储的数据。由于各自独立的窗口/线程存在并发访问的可能,所以用 localStorage 存储的数据可能较易遭遇共享访问问题(如竞态条件 Race-condition),其应被视作无锁形态的(Non-locking)(Web 存储 API 规范)。

存活时间(Duration)

localStorage API 存储的数据在整个浏览 Session 中保持存活,从而延长了可供其他系统用户访问的期限(Timeframe)。

脱机访问(Offline Access)

标准中不要求对 localStorage 数据作静态加密(Encrypted-at-rest),这意味着有可能从磁盘直接访问到这些数据。

适用场景(Use Case)

WHATWG 建议,localStorage 可用于需要跨多个窗口或 Tab 页、跨多个 Session 访问的数据,以及为了性能可能需要存储大量(几兆字节)数据的场合。

sessionStorage API

作用域(Scope)

sessionStorage API 在调用它的窗口上下文中存储数据,这意味着 Tab 1 无法访问由 Tab 2 存储的数据。

并且,与 localStorage API 类似,所有同一服务源加载的页面都可以访问 由 sessionStorage API 存储的数据,该服务源以 Scheme(https:\\)、主机(example.com )、端口(443)和域(domain/realm)(example.com)的形式定义。

这种数据访问方式与带 Secure 标志的 Cookie 类似,意味着无法通过 http 协议读取经由 https 协议存储的数据。

存续时间(Duration)

sessionStorage API 仅存储当前浏览 Session 存续期间的数据。只要 Tab 页一经关闭,数据就不再可读。如果浏览器 Tab 页被重新启用或保持打开状态,则数据不一定会被阻止访问。数据还有可能留存于内存之中,直至发生一次垃圾回收事件为止。

脱机访问(Offline Access)

标准中不要求对 sessionStorage 数据作静态加密,这意味着有可能从磁盘直接访问到这些数据。

适用场景(Use Case)

WHATWG 建议:sessionStorage 可用于与业务流程(Workflow)的某个实例相关的数据,如一次订票记录的详细信息,只是可能在其他 Tab 页同时执行着多个业务流程。这种与窗口/Tab 页绑定的特性,使得数据不会在各个 Tab 页的业务流程之间相互泄漏。

安全风险(Security Risk)

通常,保密或敏感数据不应持久存储在浏览器的数据存储中,因为在共用系统中这可能会造成信息泄漏。由于 Web 存储机制只是一些 API,所以注入脚本也有可能访问得到,这使得它的安全性要低于采用了 HttpOnly 标志的 Cookie。

虽然 sessionStorage 有一种用途就是存储业务流程相关的某些数据,以供某 Tab 页/窗口在多次重新加载(Reload)之间仍能使用,但仍应该将 Web 存储 API 当作不安全的存储来看待。因此,假如业务解决方案需要采用 localStoragesessionStorage 来存储敏感数据,则应该对数据进行加密并实施重现攻击防护(Replay Protection )。

由于通过 XSS 攻击有可能访问到 Web 存储 API,所以应该用非持久性 Cookie 来保存 Session ID,并应用适当的标志来防止不安全的访问Secure), XSSHttpOnly)和CSRF问题(SameSite)。

参考文献(Reference)

Session ID 的生命周期(Life Cycle)

Session ID 的创建和校验:宽松和严格的 Session 管理(Session ID Generation and Verification: Permissive and Strict Session Management)

Web 应用程序有两种类型的 Session 管理机制,即宽松型和严格型,这与会话固定漏洞相关。宽松型机制允许 Web 应用程序最初接受用户设置的任意 Session ID 值作为合法值,并为其创建新的 Session 。而严格型机制则要求 Web 应用程序仅接受先前由该应用程序生成的 Session ID 值。

会话 Token 应尽可能交由 Web 服务器进行处理,或由经加密保证安全的随机数生成器生成。

目前最常用的机制是严格型机制(更安全,PHP 默认采用宽松型)。开发人员必须确保 Web 应用程序不会在某些场合下采用宽松型机制。Web 应用程序绝对不允许接受未经它们生成的 Session ID。如果收到陌生的 ID,就应该生成并向用户提供新的合法 Session ID。此外,这种非法场景应该被检测为可疑活动,并应生成警告。

将 Session ID 视同其他用户输入数据一样管理(Manage Session ID as Any Other User Input)

与 Web 应用程序需要处理的任何其他用户输入数据一样,Session ID 必须被视为不受信任数据,并且必须对其进行完整的验证(Validate)和校验(Verify)。根据所用的 Session 管理机制不同,Session ID 将接收于 GET 或 POST 参数、URL 或 HTTP 头(如 Cookie)之中。如果 Web 应用程序在进行处理之前未验证并滤除非法的 Session ID,则它们可能会用于发掘出其他 Web 漏洞。例如,如果 Session ID 存储在关系数据库中,则可能是 SQL 注入;如果 Session ID 由 Web 应用程序存储并返回,则可能是持久性 XSS 。

权限发生任何变化之后都得更新 Session ID(Renew the Session ID After Any Privilege Level Change)

在用户 Session 相关的权限级别发生任何变化后,Web 应用程序都必须更新(Renew)或重新生成 Session ID。最常见的必须重新生成 Session ID 的场景,就是在身份认证的过程中,因为用户的权限级别由未经认证(或匿名)状态更改为通过认证状态了。其他的常见场景也必须考虑在内,诸如修改密码、更改权限或由 Web 应用程序的普通用户角色切换为管理员角色。Web 应用程序中的所有这些关键页面,都必须略除先前的 Session ID,必须为新接收到的每个针对关键资源的请求分配新的 Session ID,并且先前的旧 Session ID 必须被销毁。

最常见的 Web 开发框架都提供了更新 Session ID 的函数和方法,诸如 request.getSession(true)HttpSession.invalidate()(J2EE)、Session.Abandon()Response.Cookies.Add(new...)(ASP .NET)、session_start()session_regenerate_id(true)(PHP)。

为了防止会话固定攻击,必须要重新生成 Session ID。在发生这种攻击时,攻击者并非像大多数其他基于 Session 的攻击那样收集受害者的 Session ID,而是在受害用户的 Web 浏览器上设置 Session ID,并且与用 HTTP 协议还是 HTTPS 协议没有关系。这种防护措施可以缓解其他同样可启动会话固定攻击的 Web 漏洞的不利影响,比如拆分 HTTP 响应或 XSS(请参阅此处此处)。

还有一条补充建议,即在身份认证前后使用不同的 Session ID 或 Token 名称(或 Session ID 集),以便 Web 应用程序可同时对匿名用户和认证用户都进行跟踪记录,而不会有泄露 Session 或穿越两种状态绑定用户 Session 的风险。

多 Cookie 的注意事项(Considerations When Using Multiple Cookies)

如果 Web 应用程序采用 Cookie 作为 Session ID 的交换机制,并且为单个 Session 设置了多个 Cookie,则 Web 应用程序必须在允许访问用户 Session 之前对全部 Cookie 进行校验(并强行设置他们之间的关系)。

很常见的做法就是,在未经认证之前(Pre-authentication),Web 应用程序通过 HTTP 协议设置用户 Cookie,以便对未认证(或匿名)用户进行跟踪记录。一旦用户在 Web 应用程序中通过了身份认证,则会通过 HTTPS 协议设置新的认证后(Post-authentication)的安全 Cookie,并为这两种身份的 Cookie 和用户 Session 建立绑定关系。如果 Web 应用程序不为已认证 Session 对两种身份的 Cookie 进行校验,则攻击者即可利用认证前不受保护的 Cookie 访问认证后的用户 Session 了。请参阅此处此处

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

Session 到期(Session Expiration)

为了尽量缩短攻击者能在活动 Session 上发起攻击并劫持的时间区间,必须为每个 Session 设置到期时间,让 Session 具备确定的存活时长。若 Web 应用程序的 Session 到期时间设置不当,将会增加其他 Session 攻击的暴露机会。因为攻击者要能重用合法 Session ID 并劫持关联的 Session ,则该 Session 必须是在活动状态下的。

Session 间隔越短,攻击者必须利用合法 Session ID 的时间就越短。 Session 的到期时间必须根据 Web 应用程序的目的和特性进行设置,还要在安全性和可用性之间取得平衡。这样用户 Session 才不会频繁过期,他才能在 Web 应用程序中顺畅地完成操作。

空闲超时时间(Idle Timeout)和绝对超时时间(Absolute Timeout)都高度依赖于 Web 应用程序及其数据的重要程度。对于高价值应用程序而言,常用的空闲超时范围为2-5分钟,低风险应用则常为15-30分钟。

Session 到期时,Web 应用程序必须采取主动措施让客户端和服务器两端的 Session 均失效。从安全角度来看,服务器端的失效是最要紧和强制性的。

对于绝大多数 Session 交换机制而言,客户端能让 Session ID 失效的操作都是基于清除 Token 值的。比如要让 Cookie 失效,推荐给 Session ID 赋一个空(或无效)值,并将 Expires(或 Max-Age)属性置为过去的日期(如果用的是持久性 Cookie):Set-Cookie: id=; Expires=Friday, 17-May-03 18:45:00 GMT

为了在服务器端关闭 Session 并使其失效,Web 应用程序必须在 Session 到期时采取主动操作,或者用户主动用 Session 管理机制提供的函数和方法进行签退。这些函数有 HttpSession.invalidate()(J2EE)、Session.Abandon()(ASP .NET)、session_destroy()/unset()(PHP)。

Session 自动到期(Automatic Session Expiration)

空闲超时时间(Idle Timeout)

所有 Session 都应设置空闲或不活动的超时时间。空闲超时时间定义了 Session 在没有活动的情况下将会保持活跃的时长,自 Web 应用程序接受到某 Session ID 的上次HTTP 请求以来,如果超过了已定义的空闲时长则会关闭 Session 并使其失效。

空闲超时时间限制了攻击者猜测并使用其他用户合法 Session ID 的时机。但如果攻击者能够劫持某现有 Session ,则空闲超时时间就无法限制攻击者的操作,因为他可以定期在 Session 上生成活跃操作以保持更长的 Session 活跃时间。

必须在服务器端强制实施 Session 超时管理和到期机制。如果用客户端来强制设置 Session 超时时间,比如用 Session Token 或其他客户端参数来记录参照时间(如自登录时间以来的分钟数),则攻击者可能会操纵这些参数来延长 Session 的持续时间。

绝对超时时间(Absolute Timeout)

无论是否活跃,所有 Session 都应该设置绝对超时时间。绝对超时时间定义了某 Session 可以保持活跃的最大时长,自该 Session 第一次由 Web 应用程序创建开始计算;超过此预定义的绝对时长后, Session 将会关闭并失效。该 Session 失效之后,用户会被强制要求在该 Web 应用程序中再次(重新)进行身份认证并建立一个新 Session。

Session 的绝对超时时间限制了攻击者使用被劫持 Session 及模拟受害用户的时长。

更新超时时间(Renewal Timeout)

或者,Web 应用程序可以再多设置一项更新超时时间,更新超时之后 Session ID 会在用户 Session 期间进行自动更新,并且与 Session 是否活动无关,因此也与空闲超时时间的设置无关。

自第一次创建 Session 开始,经过指定时长之后,Web 应用程序可以为用户 Session 重新生成新的 ID,并尝试在客户端上设置或更新它。在客户端得知新 ID 并开始使用它之前,先前的 Session ID 值仍会有效一段时间,以留出一定的安全时间间间隔(Safety Interval)。此时,如果客户端在当前 Session 内切换到新的 ID,则应用程序应让先前的 ID 失效。

即使受害用户的 Session 仍处于活跃状态,此方案也可以最大限度地减少已有 Session ID 可被用于劫持用户 Session 的时间,这个 ID 很可能已被攻击者获取到了。虽然每次更新超时时间到期后,在 Session 存续期内其关联的 Session ID 值都会透明地定期更新,但是用户 Session 在合法的客户端上仍会保持存活和打开状态。因此,更新超时时间设置与空闲超时时间和绝对超时时间形成了互补,尤其是当绝对超时时间明显过长时(比如有个应用程序需要用户 Session 长期保持打开状态)。

根据实现的方式不同,以下竞态条件是有可能存在的。在更新超时时间刚刚到期时,持有先前合法 Session ID 的攻击者抢在受害用户之前发送请求,并且抢先获得了已更新的 Session ID。至少在这种情况下,受害用户可能会得知此次攻击,因为其 Session 会突然终止,原因是与其关联的 Session ID 不再有效了。

Session 手动到期(Manual Session Expiration)

Web 应用程序应该提供一种机制,以便对安全性很敏感的用户能在用完 Web 应用程序之后主动关闭其 Session。

签退按钮(Logout Button)

Web 应用程序必须提供一个明显可见的、易于访问的签退(Logout)(注销、退出或关闭 Session)按钮,该按钮可出现在 Web 应用程序的页面头部或菜单上,并且从每个 Web 应用程序资源和页面均可访问到,以便用户随时可以手动关闭 Session。正如 Session 到期 一节所述,Web 应用程序必须至少能在服务器端让 Session 失效。

注意

不幸的是,并非所有 Web 应用程序都能很方便地让用户关闭其当前 Session。因此,客户端的增强功能可以尽力提供关闭手段,让严谨的用户能够保护他们的 Session。

Web 浏览内容缓存(Web Content Caching)

即使在 Session 关闭以后,通过 Web 浏览器的缓存也有可能访问到 Session 内交换的私密或敏感数据。因此,Web 应用程序必须对通过 HTTP 和 HTTPS 协议交换的所有 Web 传输数据采用限制性(Restrictive)缓存指令。比如 Cache-ControlPragma HTTP 头,并且在所有或至少是敏感页面上加上等价的 META 标记。

如果 Web 应用程序的内容允许缓存,则绝不允许对 Session ID 进行缓存,这与 Web 应用程序定义的缓存策略无关。因此强烈建议采用 Cache-Control: no-cache="Set-Cookie, Set-Cookie2" 指令,以便让 Web 客户端不对 Session ID 进行缓存,参见此处

Session 管理可利用的其他客户端防护措施(Additional Client-Side Defenses for Session Management)

除了上述 Session 管理防护措施之外,Web 应用程序还可以利用客户端的其他一些措施作为补充。客户端防护措施(通常采用 JavaScript 检查和验证的形式)并非刀枪不入(Bullet Proof),而是很容易被训练有素的攻击者击破,但却可以多加一层入侵者无法绕开的防御措施。

初次登录超时(Initial Login Timeout)

Web 应用程序可以用登录页面中的 JavaScript 代码来估算和计量自页面加载及授予 Session ID 之后的时长。如果在指定时长之后再尝试登录,则客户端代码可以通知用户已经超过了登录最大时长并重新加载登录页面,从而能获取一个新的 Session ID。

这种额外的防护机制试图强制更新未认证之前的 Session ID,从而避免受害者重用之前同一台计算机的用户(或手动设置)的 Session ID,在会话固定攻击时就会发生这种情况。

在 Web 浏览器窗口关闭事件中强行签退 Session(Force Session Logout On Web Browser Window Close Events)

Web 应用程序可以用 JavaScript 代码捕获所有 Web 浏览器 Tab 页或窗口的关闭(甚至返回)事件,并在关闭 Web 浏览器之前采取适当的操作来关闭当前 Session,可以是模拟用户通过签退按钮手动关闭 Session。

禁用 Web 浏览器的跨 Tab 页 Session(Disable Web Browser Cross-Tab Sessions)

只要用户登录成功且 Session 一经建立,如果对同一个 Web 应用打开了新的 Web 浏览器 Tab 页或窗口,Web 应用程序就可以用 JavaScript 代码强制用户重新进行身份认证。Web 应用程序不希望让多个 Web 浏览器 Tab 页或窗口共享同一个 Session。因此,该应用程序尽力强迫 Web 浏览器不要在多个窗口之间共享相同的 Session ID。

注意

如果 Session ID 是通过 Cookie 交换的,则上述防护机制就无法实现,因为 Cookie 是被 Web 浏览器的所有 Tab 页/窗口共享的。

客户端自动签退(Automatic Client Logout)

在空闲超时时间到期后,Web 应用程序可以在所有(或关键)页面中用 JavaScript 代码自动签退客户端 Session,比如可让用户重定向到签退页面(以上提到过的签退按钮用到的同一个资源)。

用客户端代码增强服务器端空闲超时功能存在一个好处,即用户可以知晓 Session 已经因为没有活动而结束了,甚至可以通过倒计时和警告信息的方式提前得到 Session 即将到期的通知。如果 Session 在服务器端默默地过期,那些需要大量录入数据的网页就会丢失工作成果,这种用户友好的方式则有助于避免这种情况。

Session 攻击检测(Session Attacks Detection)

Session ID 猜测和暴力破解检测(Session ID Guessing and Brute Force Detection)

如果攻击者试图猜测或暴力破解合法的 Session ID,他需要用来自单个(或一组)IP 地址的不同 Session ID 对目标 Web 应用程序发起一连串的多次请求。此外,如果攻击者试图分析 Session ID 的可预测性(比如用统计学分析),他需要针对目标 Web 应用程序从单个(或一组)IP 地址发起一连串的多次请求以收集新的合法 Session ID。

依据对不同 Session ID 进行收集(或使用)的次数,Web 应用程序必须有能力检测到上述两种情况,并对违规 IP 地址发出警告并阻止(Block)访问。

检测 Session ID 异常(Detecting Session ID Anomalies)

Web 应用程序应该重点关注与 Session ID 有关的异常检测,比如被操纵的 ID。 OWASP AppSensor 项目 提供了一种框架和方法,可在 Web 应用程序中实现内置的入侵检测功能,该项目以设置多个检测点并查看响应操作的形式专注于检测异常和意外行为。有时,业务逻辑的细节和高级算法(Intelligence)只能由 Web 应用程序内部获取,用外部保护层无法得知。可以在内部建立多个与 Session 相关的检测点,比如修改或删除已有 Cookie 时、已加入新的 Cookie 时、重新使用来自其他用户的 Session ID 时、Session 期间用户位置或 User-Agent 发生变化时。

把 Session ID 与其他属性绑定(Binding the Session ID to Other User Properties)

为了检测(某些情况下则是防止)用户的错误行为和 Session 劫持,强烈建议将 Session ID 与其他用户端或客户端属性绑定,比如客户端 IP 地址、User-Agent 信息或基于客户端的数字证书。如果 Web 应用程序在已建立的 Session 当中检测到这么多不同属性中有任何更改或异常,那就是很不错的企图操纵和劫持 Session 的指示,这点证据足可用于发出警告并终止可疑 Session 了。

虽然 Web 应用程序无法用上述属性可靠地防御 Session 攻击,但他们大大增加了 Web 应用程序的检测(和保护)能力。当然,训练有素的攻击者能够通过复用分配给受害用户的相同 IP 地址来跳过这些控制,复用方法可以是共享同一网络(在 NAT 环境中很常见,如 Wi-Fi 热点);或者使用相同的 Web 代理出口(Outbound)(在公司环境中很常见);或是手动修改其 User-Agent 字段,使其看起来与受害用户完全一样。

用日志记录 Session 的生命周期:监控 Session ID 的创建、使用和销毁过程(Logging Sessions Life Cycle: Monitoring Creation, Usage, and Destruction of Session IDs)

Web 应用程序应该提高日志记录能力,将有关 Session 全生命周期的信息纳入日志。尤其建议要记录 Session 相关的事件,如 Session ID 的创建、续期和销毁,以及以下过程中 Session ID 的使用细节:登录和签退操作、 Session 内的权限级别更改、超时到期、非法 Session 活动(当检测到时)和 Session 存续期间的关键业务操作。

日志的细节可以包括:时间戳、源IP地址、请求的 Web 目标资源(以及 Session 操作中涉及的资源)、HTTP 头(含 User-Agent 和 Referer 字段)、GET 和 POST 参数、错误代码和信息、用户名(或用户 ID),再加上 Session ID(Cookie、URL、GET、POST 等)。

为了保护 Session 日志免遭本地或远程的 Session ID 泄露及未经授权的访问,日志中不应包含 Session ID 之类的敏感数据。不过,某些 Session 相关(Session-specific)信息还是必须记入日志的,以便将日志条目与相关 Session 相关联。建议记录 Session ID 的加盐哈希值(Salted-hash)而不是 Session ID 本身,这样既能在日志中关联上 Session 相关信息,又不会暴露 Session ID。

特别地,Web 应用程序必须对管理界面提供全面仔细的保护,因为管理界面能够管理当前所有的活动 Session 。通常,这些管理界面是供后台支持人员用来解决 Session 有关问题甚至常见问题的,方式就是扮演用户角色并像用户那样看待 Web 应用程序。

Session 日志已成为 Web 应用程序入侵检测的主要数据源之一,并且在检测到(一次或多次)攻击时,入侵防护系统也可以根据 Session 日志来自动终止 Session 及禁用用户帐户。如果启用了主动防护,则防护动作也必须记录下来。

多 Session 同时登录(Simultaneous Session Logons)

是否允许同一用户同时进行多次登录,且来自相同或不同的客户端 IP 地址,这是在设计 Web 应用程序时就要确定的。如果 Web 应用程序不希望多个 Session 同时登录,则每次新的身份认证事件之后都必须采取有效行动,自动终止先前的合法 Session ,或者向用户问清楚必须存活的 Session 有哪些(旧的、新的还是两者都要)。

建议 Web 应用程序添加供用户使用的功能,使其能随时检查活动 Session 的详细信息、监控并发登录的信息并向用户告警、提供用户手动远程终止 Session 的功能,以及记录多个客户端的详细信息以跟踪账户活动的历史(日志 Logbook),这些详细信息诸如 IP 地址、User-Agent 数据、登录日期和时间、空闲时间等。

Session 管理 WAF 防护(Session Management WAF Protections)

在某些情况下,Web 应用程序的源代码或是不可用的,或是无法修改的,或者为实现上述多项安全建议和最佳实践所需的改动要对 Web 应用程序的架构进行全部重新设计,因而无法在短期内轻松实现修改。

在这些情况下,为了补充 Web 应用程序的防御措施,也为了让 Web 应用程序尽可能安全,建议采用外部保护手段,比如能够降低已知 Session 管理风险的 Web 应用程序防火墙(WAF,Web Application Firewall)。

WAF 提供了检测和防护基于 Session 攻击的功能。一方面,WAF 强制在 Cookie 上应用安全属性,比如 SecureHttpOnly 标志、在 Web 应用程序所有设置新 Cookie 的响应头字段 Set-Cookie 中设置简单的重写规则。

另一方面,WAF 还可以实现更多高级功能:允许 WAF 跟踪 Session 及对应 Session ID,针对会话固定攻击采取全类别防护(在检测到权限变动时更新客户端的 Session ID),强制要求会话保持(Sticky Session)(对 Session ID 与其他客户端属性之间的关系进行校验,如 IP 地址或 User-Agent 信息),管理 Session 过期行为(强制客户端和 Web 应用程序全都终止 Session )。

开源的 ModSecurity WAF 再加上 OWASP 核心规则集,已能提供的功能包括:检测并设置 Cookie 安全属性的能力、防护 Session 固定攻击的对策、为强制要求会话保持而进行的 Session 跟踪功能。

作者及主要编辑人员(Authors and Primary Editors)

Raul Siles (DinoSec) - raul@dinosec.com

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!