【问题标题】:PHP Session Fixation / HijackingPHP 会话固定/劫持
【发布时间】:2011-07-02 03:33:55
【问题描述】:

我正在尝试更多地了解 PHP Session Fixation 和劫持以及如何防止这些问题。我一直在 Chris Shiflett 的网站上阅读以下两篇文章:

但是,我不确定我是否理解正确。

为了帮助防止会话固定,成功登录后拨打session_regenerate_id(true); 是否足够?我想我理解正确。

他还谈到了使用通过 $_GET 在 url 中传递的令牌来防止会话劫持。这将如何完成?我猜当有人登录时,您会生成他们的令牌并将其存储在会话变量中,然后在每个页面上将该会话变量与 $_GET 变量的值进行比较?

这个令牌是否只需要在每个会话或每次页面加载时更改一次?

还有一种无需在 URL 中传递值即可防止劫持的好方法?这样会容易很多。

【问题讨论】:

  • 也许您可以添加指向您找到这些建议的页面的链接。

标签: php security session session-cookies


【解决方案1】:

好的,有两个独立但相关的问题,每个问题的处理方式不同。

会话固定

这是攻击者为用户显式设置会话的会话标识符的地方。通常在 PHP 中,它是通过给他们一个像 http://www.example.com/index...?session_name=sessionid 这样的 URL 来完成的。一旦攻击者将 url 提供给客户端,该攻击与会话劫持攻击相同。

有几种方法可以防止会话固定(全部使用):

  • 在您的php.ini 文件中设置session.use_trans_sid = 0。这将告诉 PHP 不要在 URL 中包含标识符,并且不要读取 URL 中的标识符。

  • 在您的php.ini 文件中设置session.use_only_cookies = 1。这将告诉 PHP 永远不要使用带有会话标识符的 URL。

  • 在会话状态发生变化时重新生成会话 ID。这意味着以下任何一种:

    • 用户身份验证
    • 在会话中存储敏感信息
    • 更改会话的任何内容
    • 等等……

会话劫持

这是攻击者获取会话标识符并能够像该用户一样发送请求的地方。这意味着,由于攻击者拥有标识符,就服务器而言,他们与有效用户几乎没有区别。

您无法直接阻止会话劫持。但是,您可以加入一些步骤,使其变得非常困难和难以使用。

  • 使用强会话哈希标识符:session.hash_function in php.ini。如果 PHP session.hash_function = 1 用于 SHA1。如果 PHP >= 5.3,请将其设置为 session.hash_function = sha256session.hash_function = sha512

  • 发送强哈希:session.hash_bits_per_character in php.ini。将此设置为session.hash_bits_per_character = 5。虽然这并没有使破解变得更难,但当攻击者试图猜测会话标识符时,它确实会有所作为。 ID 会更短,但会使用更多字符。

  • 在您的php.ini 文件中使用session.entropy_filesession.entropy_length 设置额外的熵。将前者设置为session.entropy_file = /dev/urandom,将后者设置为将从熵文件中读取的字节数,例如session.entropy_length = 256

  • 从默认 PHPSESSID 更改会话名称。这是通过在调用session_start 之前使用您自己的标识符名称作为第一个参数调用session_name() 来完成的。

  • 如果您真的偏执,您也可以轮换会话名称,但请注意,如果您更改此名称(例如,如果您使其依赖于时间)。但根据您的用例,它可能是一种选择......

  • 经常轮换您的会话标识符。我不会在每个请求中都这样做(除非你真的需要那种级别的安全性),而是随机间隔。您希望经常更改此设置,因为如果攻击者确实劫持了会话,您不希望他们能够使用它太长时间。

  • 在会话中包含 user agent from $_SERVER['HTTP_USER_AGENT']。基本上,当会话开始时,将其存储在$_SESSION['user_agent'] 之类的地方。然后,在每个后续请求中检查它是否匹配。请注意,这可能是伪造的,因此它不是 100% 可靠的,但总比没有好。

  • 在会话中包含 user's IP address from $_SERVER['REMOTE_ADDR']。基本上,当会话开始时,将其存储在$_SESSION['remote_ip'] 之类的地方。对于一些为其用户使用多个 IP 地址的 ISP(例如 AOL 过去所做的),这可能是个问题。但是如果你使用它,它会更加安全。攻击者伪造 IP 地址的唯一方法是在真实用户和您之间的某个时间点破坏网络。如果他们破坏了网络,他们可以做的比劫持(如 MITM 攻击等)更糟糕。

  • 在会话和浏览器端包含一个令牌,您会经常增加和比较。基本上,对于每个请求,请在服务器端执行$_SESSION['counter']++。也在浏览器端的 JS 中做一些事情来做同样的事情(使用本地存储)。然后,当您发送请求时,只需获取令牌的 nonce,并验证服务器上的 nonce 是否相同。通过这样做,您应该能够检测到被劫持的会话,因为攻击者将没有确切的计数器,或者如果他们有,您将有 2 个系统传输相同的计数并且可以判断一个是伪造的。这不适用于所有应用程序,但它是解决问题的一种方法。

关于两者的说明

会话固定和劫持之间的区别仅在于会话标识符是如何被破坏的。在固定状态下,标识符被设置为攻击者事先知道的值。在劫持中,它要么被猜到,要么被用户窃取。否则,一旦标识符被泄露,两者的效果是相同的。

会话 ID 重新生成

每当您使用session_regenerate_id 重新生成会话标识符时,都应该删除旧会话。这通过核心会话处理程序透明地发生。然而,一些custom session handlers using session_set_save_handler() 不这样做,并且可以对旧会话标识符进行攻击。确保如果您使用的是自定义会话处理程序,则跟踪您打开的标识符,并且如果它与您保存的标识符不同,则明确删除(或更改)旧标识符上的标识符。

使用默认会话处理程序,您只需调用session_regenerate_id(true) 即可。这将为您删除旧的会话信息。旧 ID 不再有效,如果攻击者(或其他任何人)试图使用它,将导致创建一个新会话。不过要小心自定义会话处理程序....

销毁会话

如果您要销毁会话(例如在注销时),请确保彻底销毁它。这包括取消设置 cookie。使用session_destroy

function destroySession() {
    $params = session_get_cookie_params();
    setcookie(session_name(), '', time() - 42000,
        $params["path"], $params["domain"],
        $params["secure"], $params["httponly"]
    );
    session_destroy();
}

【讨论】:

  • 每个字符使用 5 位而不是 4 位不会以任何方式改变“强度”(无论“强度”在这种情况下是什么意思)。但是,尽管您的观点总体上是可取的,但它们缺少一些重要的细节。例如,与旧会话 ID 关联的会话会发生什么情况,或者具有旧会话 ID 的会话在无效后应如何处理。
  • @battal:不,这就是重点。 session_regenerate_id 不会使仍与旧 ID 关联的会话失效;只有当 delete_old_session 参数设置为 true 时,会话才会被销毁。但是如果攻击者启动了这个 ID 重新生成呢?
  • 我不同意每次更改会话变量时重新生成会话,它应该只在登录/注销时进行。同样检查用户代理是没有意义的,检查 REMOTE_ADDR 是有问题的。我想补充的一件事是session.entropy_file = /dev/urandom。 PHP 的内部熵生成被证明是非常弱的,而 /dev/random 或 /dev/uranom 提供的熵池是您在没有硬件 rng 的 Web 服务器上可以获得的最好的。
  • 另外你应该添加session.cookie_httponlysession.cookie_secure。第一个有助于阻止 xss(但它并不完美)。第二种是阻止 OWASP A9 的最佳方法...
  • 不明白这么好的答案,但错过了最重要的部分:使用 SSL/HTTPS。计数器增量是多个请求彼此快速的问题根源,用户刷新页面两次或点击提交按钮两次。 IP 地址解决方案是当今所有移动用户和不断变化的 IP 的问题。你可以看看第一组IP,但它仍然在自找麻烦。最好的办法是首先防止发现会话 ID,即使用 SSL/HTTPS。
【解决方案2】:

两种会话攻击的目标相同:获得对另一个用户合法会话的访问权限。但攻击向量不同:

在这两种攻击中,会话 ID 都是这些攻击所针对的敏感数据。因此,对于读取访问(会话劫持)和写入访问(会话固定)都需要保护会话 ID。

使用 HTTPS 保护敏感数据的一般规则也适用于这种情况。此外,您应该执行以下操作:

为防止 Session Fixation 攻击,请确保:

为防止 Session Hijacking 攻击,请确保:

为防止两种会话攻击,请确保:

  • 只接受您的应用程序已启动的会话。您可以通过使用客户端特定信息在启动时对会话进行指纹识别来做到这一点。您可以使用 User-Agent ID,但不要使用远程 IP 地址或任何其他可能在请求之间发生变化的信息。
  • 在身份验证尝试(true 仅在成功时)或更改权限后使用 session_regenerate_id(true) 更改会话 ID 并销毁旧会话。 (如果要保留与旧 ID 关联的会话,请确保在重新生成 ID 之前使用 session_write_close 存储 $_SESSION 的任何更改;否则只有具有新 ID 的会话会受到影响通过这些变化。)
  • 使用正确的会话到期实现(请参阅How do I expire a PHP session after 30 minutes?)。

【讨论】:

  • 很棒的帖子,尤其是最后一节。
【解决方案3】:

你提到的标记是一个“nonce” - 使用一次的数字。它们不一定必须只使用一次,但它们使用的时间越长,nonce 被捕获并用于劫持会话的几率就越高。

nonce 的另一个缺点是很难构建一个使用它们并允许多个并行窗口在同一个窗体上的系统。例如用户在论坛上打开两个窗口,然后开始处理两个帖子:

window 'A' loads first and gets nonce 'P'
window 'B' loads second and gets nonce 'Q'

如果您无法跟踪多个窗口,您将只存储一个随机数 - 窗口 B/Q。当用户随后从窗口 A 提交他们的帖子并传入 nonce 'P' 时,系统将拒绝该帖子为P != Q

【讨论】:

  • 那么这与会话固定有什么关系?
  • 他有一个正确的观点,尤其是在同时使用许多 AJAX 请求的领域。
【解决方案4】:

我没有阅读 Shiflett 的文章,但我认为你误解了一些东西。

默认情况下,只要客户端不接受 cookie,PHP 就会在 URL 中传递会话令牌。否则,在最常见的情况下,会话令牌存储为 cookie。

这意味着如果您在 URL 中放置一个会话令牌,PHP 将识别它并随后尝试使用它。当某人创建会话然后通过打开包含会话令牌的 URL 来欺骗另一个用户共享同一会话时,就会发生会话固定。如果用户以某种方式进行身份验证,那么恶意用户就会知道经过身份验证的用户的会话令牌,该用户可能具有不同的权限。

我确信 Shiflett 解释过,通常的做法是在每次用户权限更改时重新生成不同的令牌。

【讨论】:

  • 要补充这一点,请务必销毁任何以前打开的会话,因为它们在现有用户权限下仍然有效。
【解决方案5】:

是的,您可以通过在登录时重新生成会话 ID 来防止会话固定。这样,如果攻击者将不知道新认证会话的 cookie 值。另一种完全解决问题的方法是在运行时配置中设置session.use_only_cookies=True。攻击者无法在另一个域的上下文中设置 cookie 的值。会话固定依赖于将 cookie 值作为 GET 或 POST 发送。

【讨论】:

    猜你喜欢
    • 2012-02-20
    • 1970-01-01
    • 1970-01-01
    • 2011-09-22
    • 1970-01-01
    • 2011-12-09
    • 1970-01-01
    • 2013-01-17
    • 2013-10-09
    相关资源
    最近更新 更多