PHP会话修复/劫持


145

我试图了解有关PHP 会话修复和劫持以及如何防止这些问题的更多信息。我一直在Chris Shiflett的网站上阅读以下两篇文章:

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

为了帮助防止会话固定,只需调用session_regenerate_id(true);就可以了。成功登录某人后?我想我理解正确。

他还讨论了如何使用通过$ _GET在URL中传递的令牌来防止会话劫持。究竟该怎么做?我猜想有人登录时会生成他们的令牌并将其存储在会话变量中,然后在每个页面上将该会话变量与$ _GET变量的值进行比较吗?

每个会话或每次加载页面时仅需要更改一次此令牌吗?

它们也是防止劫持而不必在URL中传递值的好方法吗?这会容易得多。


也许您可以将链接添加到找到这些建议的页面。
Gumbo

Answers:


219

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

会话固定

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

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

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

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

  • 每当会话状态更改时,都会重新生成会话ID。这意味着以下任何一项:

    • 用户认证
    • 在会话中存储敏感信息
    • 更改有关会话的任何内容
    • 等等...

会话劫持

在这里,攻击者可以获取会话标识符,并能够像发送该用户一样发送请求。这意味着,由于攻击者具有标识符,因此就服务器而言,它们几乎与有效用户没有区别。

您不能直接阻止会话劫持。但是,您可以采取措施使其变得非常困难和难以使用。

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

  • 发送一个强大的哈希值:session.hash_bits_per_characterphp.ini。将此设置为session.hash_bits_per_character = 5。尽管这不会使破解变得更加困难,但在攻击者试图猜测会话标识符时,确实有所作为。ID将更短,但使用更多字符。

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

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

  • 如果您确实很偏执,也可以旋转会话名称,但是请注意,如果更改此名称,则所有会话将自动失效(例如,如果您依赖于时间)。但是根据您的用例,它可能是一个选择...

  • 经常轮换您的会话标识符。我不会对每个请求都执行此操作(除非您确实需要该级别的安全性),而是以随机间隔进行。您希望经常更改此设置,因为如果攻击者确实劫持了会话,则您不希望他们能够使用它太长时间。

  • 在会话中包括用户代理$_SERVER['HTTP_USER_AGENT']。基本上,当会话开始时,将其存储在$_SESSION['user_agent']。然后,在每个后续请求上检查其是否匹配。请注意,这可能是伪造的,因此并非100%可靠,但总比没有好。

  • 在会话中包括用户的IP地址$_SERVER['REMOTE_ADDR']。基本上,当会话开始时,将其存储在$_SESSION['remote_ip']。对于某些为其用户使用多个IP地址的ISP(例如曾经使用过的AOL),这可能会出现问题。但是,如果使用它,它将更加安全。攻击者伪造IP地址的唯一方法是在真实用户和您之间的某个时候破坏网络。而且,如果它们危害了网络,它们的后果可能远不如劫持(例如MITM攻击等)。

  • 在会话中以及您经常增加和比较的浏览器端添加令牌。基本上,对于每个请求都$_SESSION['counter']++在服务器端执行。也可以在浏览器端的JS中执行某些操作(使用本地存储)。然后,当您发送请求时,只需简单地获取令牌的现时值,并验证该现时值在服务器上是否相同。这样,由于攻击者将没有确切的计数器,因此您应该能够检测到被劫持的会话,或者如果攻击者没有,您将有2个系统传输相同的计数并可以辨别出一个伪造的系统。这不适用于所有应用程序,但这是解决问题的一种方法。

关于两者的说明

会话固定和劫持之间的区别仅在于如何损害会话标识符。在固定中,将标识符设置为攻击者事先知道的值。在“劫持”中,它是被用户猜测或被盗的。否则,一旦标识符受损,两者的作用是相同的。

会话ID再生

每当您使用session_regenerate_id旧会话重新生成会话标识符时,都应将其删除。这对于核心会话处理程序是透明的。但是,某些使用的自定义会话处理程序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();
}

4
每个字符使用5位而不是4位不会以任何方式更改“强度”(在这种情况下,“强度”意味着什么)。但是尽管您的观点总体上是可取的,但它们缺少一些重要的细节。例如,与旧会话ID关联的会话发生了什么,或者在具有旧会话ID的会话无效后应如何处理。
Gumbo'2

2
@battal:不,这就是重点。session_regenerate_id不会使仍与旧ID相关联的会话无效;仅当delete_old_session参数设置为true时,会话才会被破坏。但是,如果攻击者启动了此ID重新生成怎么办?
Gumbo'2

6
我不同意每次更改会话变量时的会话重新生成,仅应在登录/注销时完成。同样,检查用户代理是没有意义的,并且检查REMOTE_ADDR是有问题的。我想补充的一件事是session.entropy_file = /dev/urandom。经证明,PHP的内部熵生成非常弱,并且/ dev / random或/ dev / uranom提供的熵池是在没有硬件Rng的情况下在Web服务器上获得的最佳性能。
rook

4
另外,您应该添加session.cookie_httponlysession.cookie_secure。第一个有助于阻止xss(但它并不完美)。第二个方法是阻止OWASP A9的最佳方法...
rook

4
不明白这样一个好答案,但却缺少最重要的内容:使用SSL / HTTPS。计数器增量是问题的根源,彼此之间很快就会有多个请求,用户刷新页面两次或点击提交按钮两次。如今,所有移动用户以及不断变化的IP都存在IP地址解决方案的问题。您可以看一下IP的第一组,但它仍然在麻烦。最好是从一开始就防止使用SSL / HTTPS发现会话ID。
桑内(Sanne)2013年

37

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

  • 会话固定攻击中,攻击者已经可以访问有效会话,并试图迫使受害者使用此特定会话。

  • 会话劫持攻击中,攻击者尝试获取受害者会话的ID来使用他/她的会话。

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

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

为防止会话固定攻击,请确保:

为防止会话劫持攻击,请确保:

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

  • 只接受您的应用程序发起的会话。您可以通过在启动客户端特定信息时给会话添加指纹来实现此目的。您可以使用User-Agent ID,但不要使用远程IP地址或在两次请求之间可能会更改的任何其他信息。
  • session_regenerate_id(true)在尝试进行身份验证(true仅在成功之后)或更改特权之后使用来更改会话ID ,并销毁旧会话。(如果要保留与旧ID相关联的会话,请确保重新生成ID 之前存储对$_SESSIONusing的所有更改;否则,只有具有新ID的会话才会受到这些更改的影响。)session_write_close
  • 使用正确的会话到期实现(请参阅如何在30分钟后使PHP会话到期?)。

很棒的帖子,特别是最后一节。
马蒂斯(Mattis)

6

您提到的令牌是一个“ nonce”(编号)-使用一次。它们不一定必须只使用一次,但是使用时间越长,被捕获并用于劫持会话的几率就越高。

随机数的另一个缺点是,很难构建一个使用它们的系统,并允许以相同的形式存在多个并行窗口。例如,用户在一个论坛上打开两个窗口,并开始处理两个帖子:

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

如果您无法跟踪多个窗口,则只能存储一个随机数-窗口B / Q。然后,当用户从窗口A提交帖子并传递随机数“ P”时,系统将以拒绝该帖子P != Q


那么,这与会话固定有什么关系?
rook

2
特别是在同时使用许多AJAX请求的领域中,他有道理。
DanielG 2012年

2

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

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

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

正如我确定Shiflett解释的那样,通常要做的是每次用户权限更改时都重新生成一个不同的令牌。


除此之外,请确保销毁任何先前打开的会话,因为它们在现有用户权限下仍然有效。
corrodedmonkee

0

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.