PHP 会话固定/劫持

我试图了解更多关于 PHP 固定时段和劫持以及如何防止这些问题。我在 Chris Shiflett 的网站上读到了以下两篇文章:

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

为了防止会话固定,在成功登录某人之后调用 session_regenerate_id(true);是否足够?我想我理解得没错。

他还谈到使用通过 $_GET在 url 中传递的令牌来防止会话劫持。具体该怎么做呢?我猜想,当有人登录时,你会生成他们的令牌并将其存储在一个会话变量中,然后在每个页面上,你会比较该会话变量与 $_GET变量的值?

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

还有一种防止劫持的好方法,而不必在 URL 中传递一个值吗?这样就简单多了。

70628 次浏览

您提到的标记是一个“ nonce”——使用过一次的数字。它们不一定只需要使用一次,但是使用的时间越长,就越有可能捕捉到 nonce 并用于劫持会话。

Nonce 的另一个缺点是很难构建一个使用它们并允许同一表单上有多个并行窗口的系统。例如,用户在一个论坛上打开两个窗口,开始写两篇文章:

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

如果你没有办法跟踪多个窗口,你将只存储一次-那个窗口 B/Q。当用户然后提交他们的文章从窗口 A 和传递的立即’P’,这个系统将拒绝作为 P != Q的文章。

我没有读希弗莱特的文章,但我认为你误解了一些东西。

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

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

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

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

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

固定时段

这是攻击者为用户显式设置会话的会话标识符的地方。通常在 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。这意味着以下任何一种情况:

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

会话劫持

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

你不能直接阻止会话劫持。然而,您可以添加步骤,使其非常困难和更难使用。

  • 使用强会话散列标识符: php.ini中的 session.hash_function。如果 PHP < 5.3,则将其设置为 session.hash_function = 1 for SHA1。如果 PHP > = 5.3,将其设置为 session.hash_function = sha256session.hash_function = sha512

  • 发送一个强散列: php.ini中的 session.hash_bits_per_character。设置为 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()来实现的。

  • 如果您是 真的偏执狂,您也可以旋转会话名称,但是要注意,如果您更改这个名称,所有会话将自动失效(例如,如果您使其依赖于时间)。但是根据您的用例,它可能是一个选项..。

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

  • 在会话中包含 来自 $_SERVER['HTTP_USER_AGENT']的用户代理。基本上,当会话开始时,将其存储在类似于 $_SESSION['user_agent']的内容中。然后,在每个后续请求上检查它是否匹配。注意,这可能是伪造的,所以它不是100% 可靠,但总比没有好。

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

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

关于这两个的一个注释

会话固定和劫持之间的区别仅仅在于会话标识符是如何被破坏的。在固定中,标识符被设置为攻击者事先知道的值。在劫持中,它要么是猜测的,要么是从用户那里偷来的。否则,一旦标识符被破坏,两者的效果是相同的。

会话 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();
}

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

  • 会话固定 攻击中,攻击者已经可以访问一个有效的会话,并试图强制受害者使用这个特定的会话。

  • 会话劫持攻击中,攻击者尝试获取受害者会话的 ID 以使用其会话。

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

使用 HTTPS 保护敏感数据的一般规则也适用于这种情况。此外,你应该做到以下几点:

为了防止 固定时段攻击,请确保:

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

要防止 都有会话攻击,请确保:

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