会话管理基础

会话安全

会话模块无法保证存储在会话中的信息只能被创建会话的用户查看。需要采取额外的措施来保护会话的机密性,具体取决于与会话相关的值。

需要评估会话中传输的数据的重要性,并可能部署进一步的保护措施;这通常会付出代价,例如降低用户的便利性。例如,为了保护用户免受简单的社会工程学策略,需要启用session.use_only_cookies。在这种情况下,必须在客户端无条件地启用 cookie,否则会话将无法正常工作。

有几种方法可以将现有的会话 ID 泄露给第三方。例如 JavaScript 注入、URL 中的会话 ID、数据包嗅探、对设备的物理访问等。泄露的会话 ID 使第三方能够访问与特定 ID 关联的所有资源。首先,包含会话 ID 的 URL。如果有指向外部站点或资源的链接,则包含会话 ID 的 URL 可能会存储在外部站点的引用日志中。其次,更积极的攻击者可能会监听网络流量。如果流量未加密,会话 ID 将以纯文本形式通过网络传输。解决方案是在服务器上实施 SSL/TLS 并将其对用户设置为强制使用。应使用 HSTS 来提高安全性。

注意: 即使是 HTTPS 也无法始终保护机密数据。例如,CRIME 和 Beast 漏洞可能使攻击者读取数据。此外,请注意许多网络出于审计目的使用 HTTPS MITM 代理。攻击者也可能设置这样的代理。

非自适应会话管理

PHP 的会话管理器目前默认情况下是自适应的。自适应会话管理器存在额外的风险。

session.use_strict_mode 启用,并且会话保存处理程序支持它时,未初始化的会话 ID 将被拒绝,并将创建一个新的会话 ID。这可以防止攻击者迫使用户使用已知会话 ID。攻击者可能会粘贴包含会话 ID 的链接或发送电子邮件。例如,http://example.com/page.php?PHPSESSID=123456789 如果session.use_trans_sid 启用,受害者将使用攻击者提供的会话 ID 启动会话。session.use_strict_mode 可以减轻这种风险。

警告

用户定义的保存处理程序也可以通过实现会话 ID 验证来支持严格的会话模式。所有用户定义的保存处理程序都应实现会话 ID 验证。

会话 ID cookie 可以使用域、路径、httponly、secure 以及从 PHP 7.3 开始的 SameSite 属性设置。浏览器定义了优先级。通过使用优先级,攻击者可以设置可以永久使用的会话 ID。使用session.use_only_cookies 无法解决此问题。session.use_strict_mode 可以减轻这种风险。使用session.use_strict_mode=On,未初始化的会话 ID 将被拒绝。

注意: 尽管session.use_strict_mode 减轻了自适应会话管理的风险,但攻击者可以迫使用户使用攻击者创建的已初始化会话 ID。例如 JavaScript 注入。这种攻击可以通过本手册中的建议来减轻。 通过遵循本手册,开发人员应启用session.use_strict_mode,使用基于时间戳的会话管理,并使用session_regenerate_id() 和推荐的步骤重新生成会话 ID。如果开发人员遵循所有上述步骤,则攻击者生成的会话 ID 最终将被删除。 当访问过时的会话时,开发人员应保存用户的所有活动会话数据。因为此信息将与随后的调查有关。用户应强制注销所有会话,即要求他们重新验证身份。这可以防止攻击者滥用被盗会话。

警告

访问过时的会话并不一定意味着攻击。网络不稳定和/或立即删除活动会话会导致合法用户使用过时的会话。

从 PHP 7.1.0 开始,添加了session_create_id()。可以通过使用用户 ID 作为会话 ID 的前缀来有效地访问用户的全部活动会话。在此设置中,启用session.use_strict_mode 至关重要。否则,恶意用户可以为其他用户设置恶意会话 ID。

注意: PHP 7.1.0 之前的用户使用CSPRNG,例如/dev/urandom,或random_bytes() 和哈希函数来生成新的会话 ID。session_create_id() 具有冲突检测功能,并根据会话的 INI 设置生成会话 ID。建议使用session_create_id()

会话 ID 重新生成

session.use_strict_mode 是一个很好的缓解措施,但还不够。开发人员还必须使用session_regenerate_id() 来确保会话安全。

会话 ID 重新生成可以降低被盗会话 ID 的风险,因此必须定期调用session_regenerate_id()。例如,对于安全敏感的内容,每 15 分钟重新生成一次会话 ID。即使会话 ID 被盗,合法用户的会话和攻击者的会话都将过期。换句话说,用户或攻击者访问都会生成过时的会话访问错误。

当用户权限提升时,例如在身份验证后,必须重新生成会话 ID。session_regenerate_id() 必须在将身份验证信息设置为$_SESSION 之前调用。(session_regenerate_id() 会自动保存当前会话数据,以便将时间戳等保存到当前会话。)确保只有新的会话包含身份验证标志。

开发人员不应依赖于session.gc_maxlifetime 来使会话 ID 过期。攻击者可能会定期访问受害者的会话 ID 以防止其过期,并继续利用它,包括已验证的会话。

相反,开发人员必须实现基于时间戳的会话数据管理。

警告

虽然会话管理器可以透明地管理时间戳,但此功能尚未实现。必须保留旧的会话数据,直到 GC 完成。同时,开发人员必须确保自己已删除过时的会话数据。但是,开发人员不应立即删除活动会话数据。也就是说,session_regenerate_id(true);session_destroy() 绝不应一起调用活动会话。这听起来可能很矛盾,但这是强制性要求。

session_regenerate_id() 不会默认删除过时的会话。过时的已验证会话可能存在并可用。开发人员必须防止任何人都使用过时的会话。他们必须使用时间戳自行阻止访问过时的会话数据。

警告

突然删除活动会话会产生不良副作用。当存在对 Web 应用程序的并发连接和/或网络不稳定时,会话可能会消失。

使用突然删除活动会话的方法无法检测到潜在的恶意访问。

开发人员不应立即删除过时的会话,而应在$_SESSION 中设置一个短期过期时间(时间戳),并自行阻止访问会话数据。

开发人员不应在session_regenerate_id() 之后立即阻止访问旧的会话数据。它必须在稍后的阶段被阻止。例如,对于稳定的网络,例如有线网络,几秒钟后;对于不稳定的网络,例如手机或 Wi-Fi,几分钟后。

如果用户访问了过时的会话(已过期的会话),应拒绝访问。还建议从用户的所有会话中删除已验证状态,因为它很可能代表攻击。

正确使用session.use_only_cookiessession_regenerate_id() 会导致由攻击者设置的不可删除的 cookie 造成个人 DoS。在这种情况下,开发人员可以邀请用户删除 cookie 并告知他们可能受到安全问题的影响。攻击者可以通过有漏洞的 Web 应用程序、公开/恶意的浏览器插件、物理损坏的设备等设置恶意 cookie。

警告

不要误解 DoS 风险。session.use_strict_mode=On 是保证一般会话 ID 安全性的强制要求!建议所有网站都启用session.use_strict_mode

DoS 攻击只会发生在账户遭受攻击时。应用程序中的 JavaScript 注入漏洞是最常见的原因。

会话数据删除

过时的会话数据必须无法访问并被删除。当前的会话模块没有很好地处理这个问题。

过时的会话数据应尽快被删除。但是,活动会话不能立即被删除。为了满足这些要求,开发人员必须自行实现基于时间戳的会话数据管理。

在 $_SESSION 中设置和管理过期时间戳。禁止访问过时的会话数据。当检测到对过时会话数据的访问时,建议从用户的会话中删除所有身份验证状态,并强制他们重新验证。访问过时的会话数据可能代表着攻击。为了实现这一点,开发人员必须跟踪每个用户的所以活动会话。

注意: 访问过时的会话也可能因为网络不稳定和/或网站的并发访问而发生。例如,服务器尝试通过 cookie 设置新的会话 ID,但 Set-Cookie 数据包可能由于连接丢失而未到达客户端。一个连接可能通过 session_regenerate_id() 发出新的会话 ID,但另一个并发连接可能尚未收到新的会话 ID。因此,开发人员必须在稍后阶段禁止访问过时的会话。即,基于时间戳的会话管理是强制性的。

总之,会话数据不能通过 session_regenerate_id()session_destroy() 来销毁,而必须使用时间戳来控制对会话数据的访问。让 session_gc() 从会话数据存储中删除过时数据。

会话和锁定

默认情况下,会话数据被锁定以避免竞争条件。锁定对于跨请求保持会话数据一致性是强制性的。

但是,会话锁定可以被攻击者利用来执行 DoS 攻击。为了减轻会话锁定导致的 DoS 攻击风险,请最大限度地减少锁。当会话数据不需要更新时,请使用只读会话。使用 session_start() 中的 'read_and_close' 选项。 session_start(['read_and_close'=>1]); 使用 session_commit() 在更新 $_SESSION 后尽快关闭会话。

当前会话模块 *不会* 在会话处于非活动状态时检测到对 $_SESSION 的任何修改。开发人员有责任在会话处于非活动状态时不修改 $_SESSION。

活动会话

开发人员应跟踪每个用户的所以活动会话。并通知他们有多少个活动会话,来自哪个 IP(和区域),已活动多久等。PHP 不会跟踪这些内容。开发人员应该这样做。

存在各种实现方法。一种可能的实现是设置一个数据库来跟踪所需数据并存储任何相关信息。由于会话数据会被 GC,开发人员必须注意 GC 的数据以维护活动会话数据库的一致性。

最简单的实现之一是“用户 ID 前缀的会话 ID”,并将所需信息存储在 $_SESSION 中。许多数据库对于选择字符串前缀具有良好的性能。开发人员可以为此使用 session_regenerate_id()session_create_id()

警告

永远不要使用机密数据作为前缀。如果用户 ID 是机密的,请考虑使用 hash_hmac()

警告

启用 session.use_strict_mode 是此设置的强制要求。确保已启用。否则,活动会话数据库可能会被破坏。

基于时间戳的会话管理是检测对过时会话的访问的强制要求。当检测到对过时会话的访问时,应从用户的所以活动会话中删除身份验证标志。这可以防止攻击者继续利用被盗会话。

会话和自动登录

开发人员不应为自动登录使用长生命周期会话 ID,因为它会增加被盗会话的风险。自动登录功能应由开发人员实现。

使用安全的单次哈希密钥作为自动登录密钥,使用 setcookie()。使用比 SHA-2 更强的安全哈希。例如,SHA-256 或更大,使用来自 random_bytes()/dev/urandom 的随机数据。

如果用户未经身份验证,请检查单次自动登录密钥是否有效。如果有效,请验证用户身份并设置一个新的安全单次哈希密钥。自动登录密钥只能使用一次,即永远不要重用自动登录密钥,始终生成一个新的密钥。

自动登录密钥是长生命周期的身份验证密钥,应尽可能地保护它。使用 path/httponly/secure/SameSite cookie 属性来保护它。即,除非必要,否则不要传输自动登录密钥。

开发人员必须实现禁用自动登录并删除不需要的自动登录密钥 cookie 的功能。

CSRF(跨站点请求伪造)攻击

会话和身份验证不能防止 CSRF 攻击。开发人员必须自己实现 CSRF 防护。

output_add_rewrite_var() 可用于 CSRF 防护。有关更多详细信息,请参阅手册页。

注意: 7.2.0 之前的 PHP 使用与 trans sid 相同的输出缓冲区和 INI 设置。因此,不建议在 7.2.0 之前的 PHP 版本中使用 output_add_rewrite_var()

大多数 Web 应用程序框架都支持 CSRF 防护。有关更多详细信息,请参阅 Web 应用程序框架手册。

从 PHP 7.3 开始,可以为会话 cookie 设置 SameSite 属性。这是一种额外的措施,可以减轻 CSRF 漏洞。

添加一个注释

用户贡献的笔记

此页面没有用户贡献的笔记。
To Top