PHP 会话固定/劫持
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/5081025/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
PHP Session Fixation / HiHymaning
提问by me2
I'm trying to understand more about PHP Session Fixation& hiHymaning and how to prevent these problems. I've been reading the following two articles on Chris Shiflett's website:
我正在尝试更多地了解 PHP会话固定和劫持以及如何防止这些问题。我一直在 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); after successfully logging someone in? I think I understand that correctly.
为了防止会话固定,调用 session_regenerate_id(true); 就足够了。成功登录后?我想我理解正确。
He also talks about using tokens passed along in urls via $_GET to prevent session hiHymaning. How would this be done exactly? I'm guessing when someone logs in you generate their token & store it in an session variable, then on each page you'd compare that session variable with the value of the $_GET variable?
他还谈到使用通过 $_GET 在 URL 中传递的令牌来防止会话劫持。这将如何完成?我猜当有人登录时,您会生成他们的令牌并将其存储在会话变量中,然后在每个页面上将该会话变量与 $_GET 变量的值进行比较?
Would this token need to be changed only once per session or on each page load?
每次会话或每次加载页面时,此令牌是否只需要更改一次?
Also is their a good way of preventing hiHymaning without having to pass a value along in the urls? this would be alot easier.
他们也是防止劫持而不必在网址中传递值的好方法吗?这会容易很多。
回答by ircmaxell
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
. Once the attacker gives the url to the client, the attack is the same as a session hiHymaning attack.
这是攻击者为用户显式设置会话的会话标识符的地方。通常在 PHP 中,它是通过给它们一个像http://www.example.com/index...?session_name=sessionid
. 一旦攻击者将 url 提供给客户端,则该攻击与会话劫持攻击相同。
There are a few ways to prevent session fixation (do all of them):
有几种方法可以防止会话固定(全部执行):
Set
session.use_trans_sid = 0
in yourphp.ini
file. This will tell PHP not to include the identifier in the URL, and not to read the URL for identifiers.Set
session.use_only_cookies = 1
in yourphp.ini
file. This will tell PHP to never use URLs with session identifiers.Regenerate the session ID anytime the session's status changes. That means any of the following:
- User authentication
- Storing sensitive info in the session
- Changing anything about the session
- etc...
session.use_trans_sid = 0
在您的php.ini
文件中设置。这将告诉 PHP 不要在 URL 中包含标识符,并且不要读取标识符的 URL。session.use_only_cookies = 1
在您的php.ini
文件中设置。这将告诉 PHP 永远不要使用带有会话标识符的 URL。每当会话状态发生变化时重新生成会话 ID。这意味着以下任何一项:
- 用户认证
- 在会话中存储敏感信息
- 更改有关会话的任何内容
- 等等...
Session HiHymaning
会话劫持
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 hiHymaning. You can however put steps in to make it very difficult and harder to use.
您无法直接阻止会话劫持。但是,您可以采取步骤使其变得非常难以使用。
Use a strong session hash identifier:
session.hash_function
inphp.ini
. If PHP < 5.3, set it tosession.hash_function = 1
for SHA1. If PHP >= 5.3, set it tosession.hash_function = sha256
orsession.hash_function = sha512
.Send a strong hash:
session.hash_bits_per_character
inphp.ini
. Set this tosession.hash_bits_per_character = 5
. While this doesn't make it any harderto crack, it does make a difference when the attacker tries to guess the session identifier. The ID will be shorter, but uses more characters.Set an additional entropy with
session.entropy_file
andsession.entropy_length
in yourphp.ini
file. Set the former tosession.entropy_file = /dev/urandom
and the latter to the number of bytes that will be read from the entropy file, for examplesession.entropy_length = 256
.Change the name of the session from the default PHPSESSID. This is accomplished by calling
session_name()
with your own identifier name as the first parameter prior to callingsession_start
.If you're reallyparanoid 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 reallyneed that level of security), but at a random interval. You want to change this often since if an attacker does hiHyman 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. Basically, when the session starts, store it in something like$_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.Include the user's IP address from
$_SERVER['REMOTE_ADDR']
in the session. Basically, when the session starts, store it in something like$_SESSION['remote_ip']
. This may be problematic from some ISPs that use multiple IP addresses for their users (such as AOL used to do). 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. And if they compromise the network, they can do far worse than a hiHymaning (such as MITM attacks, etc).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. Also do something in JS on the browsers side to do the same (using a local storage). 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 hiHymaned 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. This won't work for all applications, but is one way of combating the problem.
使用强会话哈希标识符:
session.hash_function
inphp.ini
。如果 PHP < 5.3,请将其设置session.hash_function = 1
为 SHA1。如果 PHP >= 5.3,请将其设置为session.hash_function = sha256
或session.hash_function = sha512
。发送一个强哈希:
session.hash_bits_per_character
inphp.ini
。将此设置为session.hash_bits_per_character = 5
。虽然这不会使破解变得更加困难,但当攻击者试图猜测会话标识符时,它确实会有所作为。ID 会更短,但会使用更多字符。使用
session.entropy_file
和session.entropy_length
在您的php.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 个系统传输相同的计数并且可以判断一个是伪造的。这不适用于所有应用程序,但它是解决问题的一种方法。
A note on the two
关于两者的说明
The difference between Session Fixation and HiHymaning 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 HiHymaning 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 using session_regenerate_id
the old session should be deleted. 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. 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.
每当您使用session_regenerate_id
旧会话重新生成会话标识符时,都应删除。这与核心会话处理程序透明地发生。然而,一些使用的自定义会话处理程序session_set_save_handler()
不这样做,并且容易攻击旧的会话标识符。确保如果您使用自定义会话处理程序,请跟踪您打开的标识符,如果它与您保存的标识符不同,则明确删除(或更改)旧标识符上的标识符。
Using the default session handler, you're fine with just calling 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. Be careful with custom session handlers though....
使用默认会话处理程序,您只需调用session_regenerate_id(true)
. 这将为您删除旧的会话信息。旧 ID 不再有效,如果攻击者(或其他任何人)尝试使用它,则会导致创建新会话。但是要小心自定义会话处理程序......
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. Using session_destroy
:
如果您要销毁会话(例如在注销时),请确保彻底销毁它。这包括取消设置 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();
}
回答by Gumbo
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 Fixationattack, the attacker already has access to a valid session and tries to force the victim to use this particular session.
In a Session HiHymaningattack, the attacker tries to get the ID of a victim's session to use his/her session.
In both attacks the session ID is the sensitive data these attack are focused on. So it's the session ID that needs to be protected for both a read access (Session HiHymaning) and a write access (Session Fixation).
在这两种攻击中,会话 ID 是这些攻击所关注的敏感数据。因此,对于读取访问(会话劫持)和写入访问(会话固定),都需要保护会话 ID。
The general rule of protecting sensitive data by using HTTPS applies in this case, too. Additionally, you should to do the following:
使用 HTTPS 保护敏感数据的一般规则也适用于这种情况。此外,您应该执行以下操作:
To prevent Session Fixationattacks, make sure that:
要防止会话固定攻击,请确保:
- the session ID is only accepted from a cookie (set session.use_only_cookiesto
true
) and make it for HTTPS only if possible (set session.cookie_securetotrue
); you can do both withsession_set_cookie_params
.
- 会话 ID 仅从 cookie 中接受(将session.use_only_cookies设置为
true
),并尽可能仅将其用于 HTTPS(将session.cookie_secure设置为true
);你可以用session_set_cookie_params
.
To prevent Session HiHymaningattacks, make sure that:
为防止会话劫持攻击,请确保:
- the session ID in the cookie is only readable by your server (set session.cookie_httponlyto
true
) - an additional source of entropy is used (see session.entropy_file) as PHP's random number generator has a known weakness; many security advisories suggest at least 128 bit of entropy length (see session.entropy_length)
- a strong cryptographic hash function is used (see session.hash_function); at best it is a computationally expensive hash functionlike Whirlpoolthat for example is five times slower than MD5 and thus allows only a fifth of the number of hash operations in opposite to MD5.
- cookie 中的会话 ID 只能由您的服务器读取(将session.cookie_httponly设置为
true
) - 使用额外的熵源(参见session.entropy_file),因为PHP 的随机数生成器有一个已知的弱点;许多安全公告建议至少 128 位的熵长度(请参阅session.entropy_length)
- 使用了强大的加密散列函数(请参阅session.hash_function);充其量它是一个像Whirlpool这样的计算成本高的散列函数,例如它比 MD5 慢五倍,因此与 MD5 相比只允许散列操作数量的五分之一。
To prevent bothsession 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-AgentID but don't use the remote IP address or any other information that might change from between requests.
- 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. (Make sure to store any changes of$_SESSION
usingsession_write_close
beforeregenerating 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.) - to use a proper session expiration implementation (see How do I expire a PHP session after 30 minutes?).
- 仅接受您的应用程序已启动的会话。您可以通过在启动时使用客户端特定信息对会话进行指纹识别来完成此操作。您可以使用用户代理ID,但不要使用远程 IP 地址或任何其他可能在请求之间发生变化的信息。
session_regenerate_id(true)
在身份验证尝试(true
仅在成功时)或权限更改后使用更改会话 ID并销毁旧会话。(如果要保留与旧 ID 关联的会话,请确保在重新生成 ID之前存储$_SESSION
using 的任何更改;否则只有具有新 ID 的会话会受到这些更改的影响。)session_write_close
- 使用适当的会话过期实现(请参阅如何在 30 分钟后使 PHP 会话过期?)。
回答by Marc B
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 hiHyman 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. e.g. the user opens two windows on a forum, and starts working on two posts:
nonces 的另一个缺点是很难构建一个使用它们并允许在同一表单上有多个并行窗口的系统。例如,用户在论坛上打开两个窗口,并开始处理两个帖子:
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. When the user then submits their post from window A and passes in nonce 'P', ths system will reject the post as P != Q
.
如果您无法跟踪多个窗口,则只能存储一个随机数 - 窗口 B/Q 的随机数。当用户然后从窗口 A 提交他们的帖子并传入 nonce 'P' 时,系统将拒绝该帖子P != Q
。
回答by Andrea
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. Oherwise in the most common case the session token is stored as a cookie.
默认情况下,每当客户端不接受 cookie 时,PHP 都会在 URL 中传递会话令牌。Oherwise 在最常见的情况下,会话令牌存储为 cookie。
This means that if you put a session token in the URL PHP will recognize it and try to use it subsequently. 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. If the user authenticates in some way, the malicious user then knows the session token of an authenticated one, who might have different privileges.
这意味着如果您在 URL 中放置会话令牌,PHP 将识别它并随后尝试使用它。当有人创建一个会话,然后通过打开一个包含会话令牌的 URL 来欺骗另一个用户共享同一个会话时,就会发生会话固定。如果用户以某种方式进行身份验证,恶意用户就会知道经过身份验证的用户的会话令牌,他们可能具有不同的权限。
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 解释的那样,通常要做的事情是在每次用户权限更改时重新生成不同的令牌。
回答by rook
Yes you could prevent session fixation by regenerating the session id once upon login. This way if the attacker will not know the cookie value of the newly authenticated session. Another approach which totally stops the problem is set session.use_only_cookies=True
in your runtime configuration. An attacker cannot set the value of a cookie in the context of another domain. Session fixation is relying on sending the cookie value as a GET or POST.
是的,您可以通过在登录时重新生成会话 ID 来防止会话固定。这样,如果攻击者不知道新验证会话的 cookie 值。另一种完全解决问题的方法是session.use_only_cookies=True
在您的运行时配置中设置。攻击者无法在另一个域的上下文中设置 cookie 的值。会话固定依赖于将 cookie 值作为 GET 或 POST 发送。