简体   繁体   English

PHP 会话固定/劫持

[英]PHP Session Fixation / Hijacking

I'm trying to understand more about PHP Session Fixation and hijacking and how to prevent these problems.我试图更多地了解 PHP会话固定和劫持以及如何防止这些问题。 I've been reading the following two articles on Chris Shiflett's website:我一直在 Chris Shiflett 的网站上阅读以下两篇文章:

However, I'm not sure I'm understanding things correctly.但是,我不确定我是否正确理解了事情。

To help prevent session fixation, is it enough to call session_regenerate_id(true);为了帮助防止会话固定,调用session_regenerate_id(true);是否足够session_regenerate_id(true); after successfully logging someone in?成功登录后? I think I understand that correctly.我想我理解正确。

He also talks about using tokens passed along in urls via $_GET to prevent session hijacking.他还谈到使用通过$_GET在 URL 中传递的令牌来防止会话劫持。 How would this be done exactly?这将如何完成? I'm guessing when someone logs in you generate their token and store it in a session variable, then on each page you'd compare that session variable with the value of the $_GET variable?我猜当有人登录时,您会生成他们的令牌并将其存储在会话变量中,然后在每个页面上将该会话变量与$_GET变量的值进行比较?

Would this token need to be changed only once per session or on each page load?每次会话或每次加载页面时,此令牌是否只需要更改一次?

Also is there a good way of preventing hijacking without having to pass a value along in the URLs?还有一种防止劫持的好方法,而不必在 URL 中传递值吗? This would be a lot easier.这会容易很多。

Ok, there are two separate but related problems, and each is handled differently.好的,有两个独立但相关的问题,每个问题的处理方式都不同。

Session Fixation会话固定

This is where an attacker explicitly sets the session identifier of a session for a user.这是攻击者为用户显式设置会话的会话标识符的地方。 Typically in PHP it's done by giving them a url like http://www.example.com/index...?session_name=sessionid .通常在 PHP 中,它是通过给他们一个像http://www.example.com/index...?session_name=sessionid这样的 url 来完成的。 Once the attacker gives the url to the client, the attack is the same as a session hijacking attack.一旦攻击者将 url 提供给客户端,则该攻击与会话劫持攻击相同。

There are a few ways to prevent session fixation (do all of them):有几种方法可以防止会话固定(全部执行):

  • Set session.use_trans_sid = 0 in your php.ini file.php.ini文件中设置session.use_trans_sid = 0 This will tell PHP not to include the identifier in the URL, and not to read the URL for identifiers.这将告诉 PHP 不要在 URL 中包含标识符,并且不要读取标识符的 URL。

  • Set session.use_only_cookies = 1 in your php.ini file.php.ini文件中设置session.use_only_cookies = 1 This will tell PHP to never use URLs with session identifiers.这将告诉 PHP 永远不要使用带有会话标识符的 URL。

  • Regenerate the session ID anytime the session's status changes.每当会话状态发生变化时重新生成会话 ID。 That means any of the following:这意味着以下任何一项:

    • User authentication用户认证
    • Storing sensitive info in the session在会话中存储敏感信息
    • Changing anything about the session更改有关会话的任何内容
    • etc...等等...

Session Hijacking会话劫持

This is where an attacker gets a hold of a session identifier and is able to send requests as if they were that user.这是攻击者获得会话标识符并能够发送请求的地方,就好像他们是该用户一样。 That means that since the attacker has the identifier, they are all but indistinguishable from the valid user with respect to the server.这意味着,由于攻击者拥有标识符,因此就服务器而言,他们与有效用户几乎无法区分。

You cannot directly prevent session hijacking.您无法直接阻止会话劫持。 You can however put steps in to make it very difficult and harder to use.但是,您可以采取步骤使其变得非常难以使用。

  • Use a strong session hash identifier: session.hash_function in php.ini .使用强会话哈希标识符: php.ini session.hash_function If PHP < 5.3, set it to session.hash_function = 1 for SHA1.如果 PHP < 5.3,则将其设置为session.hash_function = 1 for SHA1。 If PHP >= 5.3, set it to session.hash_function = sha256 or session.hash_function = sha512 .如果 PHP >= 5.3,请将其设置为session.hash_function = sha256session.hash_function = sha512

  • Send a strong hash: session.hash_bits_per_character in php.ini .php.ini发送一个强哈希: session.hash_bits_per_character Set this to session.hash_bits_per_character = 5 .将此设置为session.hash_bits_per_character = 5 While this doesn't make it any harder to crack, it does make a difference when the attacker tries to guess the session identifier.虽然这不会使破解变得更加困难,但当攻击者试图猜测会话标识符时,它确实会有所作为。 The ID will be shorter, but uses more characters. ID 会更短,但会使用更多字符。

  • Set an additional entropy with session.entropy_file and session.entropy_length in your php.ini file.php.ini文件中使用session.entropy_filesession.entropy_length设置额外的熵。 Set the former to session.entropy_file = /dev/urandom and the latter to the number of bytes that will be read from the entropy file, for example session.entropy_length = 256 .将前者设置为session.entropy_file = /dev/urandom ,将后者设置为将从熵文件读取的字节数,例如session.entropy_length = 256

  • Change the name of the session from the default PHPSESSID.将会话的名称从默认的 PHPSESSID 更改。 This is accomplished by calling session_name() with your own identifier name as the first parameter prior to calling session_start .这是通过调用来实现session_name()之前调用用自己的标识符名称作为第一个参数session_start

  • If you're really paranoid you could rotate the session name too, but beware that all sessions will automatically be invalidated if you change this (for example, if you make it dependent on the time).如果您真的很偏执,您也可以轮换会话名称,但请注意,如果您更改此名称(例如,如果您使其依赖于时间),所有会话都将自动失效。 But depending on your use-case, it may be an option...但根据您的用例,它可能是一个选项......

  • Rotate your session identifier often.经常轮换您的会话标识符。 I wouldn't do this every request (unless you really need that level of security), but at a random interval.我不会每次请求都这样做(除非你真的需要那个级别的安全性),而是随机间隔。 You want to change this often since if an attacker does hijack a session you don't want them to be able to use it for too long.您希望经常更改此设置,因为如果攻击者确实劫持了会话,您不希望他们能够使用它太长时间。

  • Include the user agent from $_SERVER['HTTP_USER_AGENT'] in the session.在会话中包含来自$_SERVER['HTTP_USER_AGENT']用户代理 Basically, when the session starts, store it in something like $_SESSION['user_agent'] .基本上,当会话开始时,将其存储在类似$_SESSION['user_agent'] Then, on each subsequent request check that it matches.然后,在每个后续请求中检查它是否匹配。 Note that this can be faked so it's not 100% reliable, but it's better than not.请注意,这可以伪造,因此它不是 100% 可靠,但总比没有好。

  • Include the user's IP address from $_SERVER['REMOTE_ADDR'] in the session.在会话中包含来自$_SERVER['REMOTE_ADDR']用户 IP 地址 Basically, when the session starts, store it in something like $_SESSION['remote_ip'] .基本上,当会话开始时,将其存储在类似$_SESSION['remote_ip'] This may be problematic from some ISPs that use multiple IP addresses for their users (such as AOL used to do).某些为其用户使用多个 IP 地址的 ISP(例如 AOL 曾经这样做)可能会导致此问题。 But if you use it, it will be much more secure.但是如果你使用它,它会更安全。 The only way for an attacker to fake the IP address is to compromise the network at some point between the real user and you.攻击者伪造 IP 地址的唯一方法是在真实用户和您之间的某个时间点破坏网络。 And if they compromise the network, they can do far worse than a hijacking (such as MITM attacks, etc).如果他们破坏了网络,他们的行为可能比劫持(例如 MITM 攻击等)更糟糕。

  • Include a token in the session and on the browsers side that you increment and compare often.在会话中和浏览器端包含一个令牌,您经常增加和比较。 Basically, for each request do $_SESSION['counter']++ on the server side.基本上,对于每个请求,在服务器端执行$_SESSION['counter']++ Also do something in JS on the browsers side to do the same (using a local storage).也在浏览器端的 JS 中做一些事情来做同样的事情(使用本地存储)。 Then, when you send a request, simply take a nonce of a token, and verify that the nonce is the same on the server.然后,当您发送请求时,只需获取令牌的随机数,并验证该随机数在服务器上是否相同。 By doing this, you should be able to detect a hijacked session since the attacker won't have the exact counter, or if they do you'll have 2 systems transmitting the same count and can tell one is forged.通过这样做,您应该能够检测到被劫持的会话,因为攻击者不会有确切的计数器,或者如果他们有,您将有 2 个系统传输相同的计数并可以判断一个是伪造的。 This won't work for all applications, but is one way of combating the problem.这不适用于所有应用程序,但它是解决问题的一种方法。

A note on the two关于两者的说明

The difference between Session Fixation and Hijacking is only about how the session identifier is compromised.会话固定和劫持之间的区别仅在于会话标识符如何被破坏。 In fixation, the identifier is set to a value that the attacker knows before hand.在固定中,标识符被设置为攻击者事先知道的值。 In Hijacking it's either guessed or stolen from the user.在劫持中,它要么是从用户那里猜到的,要么是从用户那里窃取的。 Otherwise the effects of the two are the same once the identifier is compromised.否则,一旦标识符被泄露,两者的影响是相同的。

Session ID Regeneration会话 ID 重新生成

Whenever you regenerate the session identifier usingsession_regenerate_id the old session should be deleted.每当您使用session_regenerate_id重新生成会话标识符时,应删除旧会话。 This happens transparently with the core session handler.这与核心会话处理程序透明地发生。 However some custom session handlers using session_set_save_handler() do not do this and are open to attack on old session identifiers.然而,一些使用session_set_save_handler()自定义会话处理程序不会这样做,并且容易攻击旧的会话标识符。 Make sure that if you are using a custom session handler, that you keep track of the identifier that you open, and if it's not the same one that you save that you explicitly delete (or change) the identifier on the old one.确保如果您使用自定义会话处理程序,请跟踪您打开的标识符,如果它与您保存的标识符不同,则明确删除(或更改)旧标识符上的标识符。

Using the default session handler, you're fine with just calling session_regenerate_id(true) .使用默认会话处理程序,您只需调用session_regenerate_id(true) That will remove the old session information for you.这将为您删除旧的会话信息。 The old ID is no longer valid and will cause a new session to be created if the attacker (or anyone else for that matter) tries to use it.旧 ID 不再有效,如果攻击者(或其他任何人)尝试使用它,则会导致创建新会话。 Be careful with custom session handlers though....但是要小心自定义会话处理程序......

Destroying a Session销毁会话

If you're going to destroy a session (on logout for example), make sure you destroy it thoroughly.如果您要销毁会话(例如在注销时),请确保彻底销毁它。 This includes unsetting the cookie.这包括取消设置 cookie。 Usingsession_destroy :使用session_destroy

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

Both session attacks have the same goal: Gain access to a legitimate session of another user.两种会话攻击都有相同的目标:访问另一个用户的合法会话。 But the attack vectors are different:但是攻击向量是不同的:

  • In a Session Fixation attack , the attacker already has access to a valid session and tries to force the victim to use this particular session.会话固定攻击中,攻击者已经可以访问一个有效的会话并试图强迫受害者使用这个特定的会话。

  • In aSession Hijacking attack , the attacker tries to get the ID of a victim's session to use his/her session.会话劫持攻击中,攻击者试图获取受害者会话的 ID 以使用他/她的会话。

In both attacks the session ID is the sensitive data these attack are focused on.在这两种攻击中,会话 ID 是这些攻击所关注的敏感数据。 So it's the session ID that needs to be protected for both a read access (Session Hijacking) and a write access (Session Fixation).因此,对于读取访问(会话劫持)和写入访问(会话固定),都需要保护会话 ID。

The general rule of protecting sensitive data by using HTTPS applies in this case, too.使用 HTTPS 保护敏感数据的一般规则也适用于这种情况。 Additionally, you should to do the following:此外,您应该执行以下操作:

To prevent Session Fixation attacks, make sure that:要防止会话固定攻击,请确保:

To prevent Session Hijacking attacks, make sure that:为防止会话劫持攻击,请确保:

To prevent both session attacks, make sure that:要防止这两种会话攻击,请确保:

  • to only accept sessions that your application have initiated.仅接受您的应用程序已启动的会话。 You can do this by fingerprinting a session on initiation with client specific information.您可以通过在启动时使用客户端特定信息对会话进行指纹识别来完成此操作。 You can use the User-Agent ID but don't use the remote IP address or any other information that might change from between requests.您可以使用用户代理ID,但不要使用远程 IP 地址或任何其他可能在请求之间发生变化的信息。
  • to change the session ID using session_regenerate_id(true) after an authentication attempt ( true only on success) or a change of privileges and destroy the old session.在身份验证尝试(仅在成功时为true )或权限更改后使用session_regenerate_id(true)更改会话 ID 并销毁旧会话。 (Make sure to store any changes of $_SESSION using session_write_close before regenerating the ID if you want to preserved the session associated to the old ID; otherwise only the session with the new ID will be affected by those changes.) (如果要保留与旧 ID 关联的会话,请确保重新生成 ID之前使用session_write_close存储$_SESSION任何更改;否则只有具有新 ID 的会话会受到这些更改的影响。)
  • to use a proper session expiration implementation (see How do I expire a PHP session after 30 minutes? ).使用适当的会话过期实现(请参阅如何在 30 分钟后使 PHP 会话过期? )。

The tokens you mention are a "nonce" - number used once.您提到的令牌是“随机数” - 使用一次的数字。 They don't necessarily have to be used only once, but the longer they're used, the higher the odds that the nonce can be captured and used to hijack the session.它们不一定只需要使用一次,但是它们使用的时间越长,随机数被捕获并用于劫持会话的可能性就越高。

Another drawback to nonces is that it's very hard to build a system that uses them and allows multiple parallel windows on the same form. nonces 的另一个缺点是很难构建一个使用它们并允许在同一表单上有多个并行窗口的系统。 eg the user opens two windows on a forum, and starts working on two posts:例如,用户在论坛上打开两个窗口,并开始处理两个帖子:

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

If you have no way of tracking multiple windows, you'll only have stored one nonce - that of window B/Q.如果您无法跟踪多个窗口,则只能存储一个随机数 - 窗口 B/Q 的随机数。 When the user then submits their post from window A and passes in nonce 'P', ths system will reject the post as P != Q .当用户然后从窗口 A 提交他们的帖子并传入 nonce 'P' 时,系统将拒绝该帖子,因为P != Q

I did not read Shiflett's article, but I think you have misunderstood something.我没有读 Shiflett 的文章,但我认为你误解了一些东西。

By default PHP passes the session token in the URL whenever the client does not accept cookies.默认情况下,每当客户端不接受 cookie 时,PHP 都会在 URL 中传递会话令牌。 Oherwise in the most common case the session token is stored as a cookie. Oherwise 在最常见的情况下,会话令牌存储为 cookie。

This means that if you put a session token in the URL PHP will recognize it and try to use it subsequently.这意味着如果您在 URL 中放置会话令牌,PHP 将识别它并随后尝试使用它。 Session fixation happens when someone creates a session and then tricks another user to share the same session by opening a URL which contains the session token.当有人创建一个会话,然后通过打开一个包含会话令牌的 URL 来欺骗另一个用户共享同一个会话时,就会发生会话固定。 If the user authenticates in some way, the malicious user then knows the session token of an authenticated one, who might have different privileges.如果用户以某种方式进行身份验证,恶意用户就会知道经过身份验证的用户的会话令牌,他们可能具有不同的权限。

As I'm sure Shiflett explains, the usual thing to do is to regenerate a different token each time the privileges of a user change.正如我确定 Shiflett 解释的那样,通常要做的事情是在每次用户权限更改时重新生成不同的令牌。

Yes you could prevent session fixation by regenerating the session id once upon login.是的,您可以通过在登录时重新生成会话 ID 来防止会话固定。 This way if the attacker will not know the cookie value of the newly authenticated session.这样,如果攻击者不知道新验证会话的 cookie 值。 Another approach which totally stops the problem is set session.use_only_cookies=True in your runtime configuration.另一种完全解决问题的方法是在运行时配置中设置session.use_only_cookies=True An attacker cannot set the value of a cookie in the context of another domain.攻击者无法在另一个域的上下文中设置 cookie 的值。 Session fixation is relying on sending the cookie value as a GET or POST.会话固定依赖于将 cookie 值作为 GET 或 POST 发送。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM