PHP 会话 + 带盐的用户代理

发布于 2024-07-14 18:53:36 字数 443 浏览 14 评论 0原文

在过去的几天里,它一直在我的脑海中运行,但我读了一些关于如何使 PHP 会话更安全的文章。 几乎所有这些文章都说您需要在会话中使用额外的盐来保存用户代理。 像这样的事情:

$fingerprint = md5('SECRET-SALT'.$_SERVER['HTTP_USER_AGENT']);

盐会让攻击者更难劫持或任何会话。 但是为什么每次检查时都要添加盐:

md5('SECRET-SALT'.$_SERVER['HTTP_USER_AGENT']) == $_SESSION [ 'fingerprint' ]

那么为什么盐会让它更安全,因为攻击者仍然只需要用户代理(相对来说是一小组不同的用户代理)和会话 ID?

可能是我忽略了一些小事情,但无法弄清楚,让我发疯,哈哈

谢谢!

It keeps running in my mind the last couple of days, but I read some articles about how to make your PHP sessions more secure. Almost all of these articles say that you need to save the useragent in the session WITH an additional salt. Something like this:

$fingerprint = md5('SECRET-SALT'.$_SERVER['HTTP_USER_AGENT']);

The salt would make it harder for an attacker to hijack or whatever the session. But WHY add a salt every time you would check it like this:

md5('SECRET-SALT'.$_SERVER['HTTP_USER_AGENT']) == $_SESSION [ 'fingerprint' ]

So WHY would a salt make it more secure, since the attacker still only needs the useragent (which is relativly a small set of different useragents) and the sessionid?

Probably something small I'm overlooking, but can't figure it out, drives me crazy haha

Thanks!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

〃安静 2024-07-21 18:53:36

建议添加盐的原因很简单。 一般来说,当您创建此“指纹”时 - 如果您仅使用一项数据(数据集有限),那么外部黑客就更容易生成此“指纹”并劫持会话。

在上面的示例中,是的,如果攻击者同时拥有“指纹”和用户代理,那么他们将能够劫持会话。

添加盐只会使攻击者更难生成指纹,这是一种“如果他们只有一条信息,那么最后一条信息就变得无用”的情况,

我建议您添加更多东西例如,在 vBulletin(我曾经参与的一个项目)中,会话 ID 哈希(与指纹基本相同)是使用以下代码生成的。

define('SESSION_IDHASH', md5($_SERVER['HTTP_USER_AGENT'] . $this->fetch_substr_ip($registry->alt_ip))); // this should *never* change during a session

另外,会话哈希是使用以下代码生成的

md5(uniqid(microtime(), true));

。尝试识别会话

因此,要劫持会话,人们需要知道以下

  • 信息 创建会话时服务器上的时间(准确)
  • 用户 浏览器代理字符串
  • 用户的 IP 地址

他们还必须欺骗 IP地址(或至少前 2/3 个八位字节)才能做到这一点

如果他们实际上已经设法获得上述信息,那么他们很可能能够进行攻击。除了会话劫持之外,

vBulletin 本身实际上并不使用“盐”,但是,在上面的示例中,盐只是添加了有限的熵,最好总是找到尽可能多的熵。

例如,在我目前用 python 编写的内容中,我生成了一个哈希值以供 XSRF 保护使用。 以下是我使用的。

    self.key = sha1(
        self.user.username +
        self.user.password +
        settings.SECRET_KEY +
        strftime("%a, %d %b %Y %H:%M:%S +0000", gmtime())
    ).hexdigest()

它需要用户的用户名和密码、当前时间以及预设的盐来生成它。 由于盐和时间的原因,攻击者很难生成这种情况(不过,请注意,只有在使用后它会发生变化,随着时间的推移,人们不需要花太多时间才能做到这一点)如果它没有改变,则为特定用户破解它)

The reason that it's suggested to add a salt is simple. Generally, when you're creating this "fingerprint" - if you're using only one item of data, which has a limited dataset, then it makes it easier for an outside hacker to generate this, and hijack the session.

In your example above, yes, if the attacker has both the "fingerprint" and the User agent, then they will be able to hijack the session.

Adding a salt only makes it harder for an attacker to generate the fingerprint, it's a case of "if they have all but one piece of information, then the last piece of information is rendered useless)

I'd suggest that you add some more things in, for example, within vBulletin (a project I used to work on) the session ID hash (which is basically the same as the fingerprint) is generated with the following code.

define('SESSION_IDHASH', md5($_SERVER['HTTP_USER_AGENT'] . $this->fetch_substr_ip($registry->alt_ip))); // this should *never* change during a session

Also, a session hash is generated using

md5(uniqid(microtime(), true));

These are both checked when trying to identify the session

So, to hijack the session, the person would need to know the following

  • The time (exactly) on the server when the session was created
  • The users Browser agent string
  • The user's IP address

They would also have to spoof the IP address (or at least the first 2/3 octets) to be able to do this.

If they're actually at a point where they've managed to get the above information, then they're probably likely to be able to attack in other ways than just session hijacking.

vBulletin don't actually use a "salt" per se, but, in your above example, the salt is just adding a limited amount of entropy, it's always best to find as much entropy as possible.

For example, in something I'm currently writing in python, I generate a hash for usage with XSRF protection. The following is what I use.

    self.key = sha1(
        self.user.username +
        self.user.password +
        settings.SECRET_KEY +
        strftime("%a, %d %b %Y %H:%M:%S +0000", gmtime())
    ).hexdigest()

Which takes the user's username and password, the current time, and a preset salt to generate this. This would be hard for an attacker to generate due to the salt, and the time (though, do note that this is only made secure by the fact that it changes once it's used, with time, it wouldn't take much for someone to crack this for a particular user if it wasnt changing)

那请放手 2024-07-21 18:53:36

如果我理解正确的话,您想防止远程攻击者猜测会话 ID 来劫持会话吗?

如果情况并非如此,那么您就严重超出了您的能力范围 - 可以窥探流量的攻击者也可以模仿用户代理,并且获得对您的会话存储的访问权限的攻击者无论如何都会让您陷入困境。

如果您存储用户代理字符串以将会话“锁定”到当前用户代理,那么对其进行散列确实没有意义 - 完整用户代理字符串上的字符串比较速度更快(然后散列,然后比较),并且不会明显加快就存储而言昂贵。

我不认为存储用户代理能够提供足够的差异化 - 更好的方法是在会话开始时生成更大的 ID(具有更多位)(也许 sha1 当前时间戳 + 用户名 + 用户代理 + 某些内容),然后将其存储在 cookie 和会话中,并在每个附加请求上进行匹配。 这不会太大地改变攻击向量(您仍然需要猜测一些数字),但是通过大幅增加攻击的难度,很容易显着增加成功攻击必须猜测的位数。

更新:

其他答案顺便提到了一些内容,但关于加盐哈希值很重要:只有当您期望攻击者能够访问您存储的哈希值而不是您的代码,然后以某种方式使用它来利用它时,对哈希值进行加盐才有意义一次攻击。

这对于长期存储(通常存储在众所周知的位置)并且由难以定位的代码使用的密码来说是有意义的。

这对于您的用例来说没有意义,因为:

  1. 信息仅在会话正在进行时(超时之前)才可行,这很少超过几个小时,之后 - 即使他们获得了存储并解码了所有内容 - 会话不能被劫持,因为它已经结束了。
  2. 通常,如果攻击者能够及时访问您的会话存储,他们就可以访问您的纯文本 PHP 代码并可以看到您的 salt。
  3. 除非您将会话存储在完全不合理的地方(例如 S3 存储桶),否则哈希窃取攻击的可能性远低于许多其他有用得多的攻击。

简而言之:不要浪费时间编写会话验证代码 - PHP 内置的会话管理已经足够安全了。

If I understand correctly, you want to prevent session hijacking by a remote attacker that guesses session IDs?

If this is not the case, then you are seriously out of your depth - an attacker that can snoop the traffic can also mimic the user agent, and an attacker that gains access to your session storage has you by the balls anyway.

If you store the user agent string to "lock" the session to the current user agent, then there is really no point in hashing it - string comparison on the full user agent string is faster (then hashing and then comparing) and not significantly more expensive in terms of storage.

I don't believe storing the user agent is providing enough differentiation - something better would be to generate a larger ID (with more bits) at session start time (maybe sha1 the current time stamp + user name + user agent + something), then store that in a cookie as well as in the session and match it up on each additional request. This doesn't change the attack vector much (you still need to guess some number), but its easy to significantly increase the number of bits that must be guess for a successful attack there by massively increasing the difficulty of the attack.

Update:

Something that other answers have mentioned in passing but is important to point about salting hashes: salting your hashes only makes sense if you expect an attacker to gain access to your stored hashes but not to your code, and then somehow uses it to leverage an attack.

This makes sense to passwords that are stored for a long time, usually in a well known location, and used by code that is hard to locate.

This does not make sense for your use case because:

  1. The information is only viable while a session in progress (before timing out) this is rarely more than a few hours, after which - even if they got the storage and decoded everything - the session cannot be hijacked because it is over.
  2. Usually if the attacker has timely access to your session storage, they have access to your plain text PHP code and can see your salt.
  3. Unless you store your sessions in a completely unreasonable place (such as an S3 bucket), a hash stealing attack is mind boggingly less likely than a lot of other attacks that will be a lot more useful.

In short: don't waste your time writing session verification code - PHP built-in session management is already secure enough.

眼泪都笑了 2024-07-21 18:53:36

如果您在自己的服务器上,加密会话变量是没有意义的,因为它们不会离开服务器。 请参阅 Linead当用户登录时,我需要在 php 会话中存储什么? 了解更多信息。 如果您位于共享服务器中,除了会话 ID 之外,您可能还需要加密每个会话变量,因为它们存储在所有邻居正在使用的同一 Web 服务器可读的临时文件中。

无论如何,如果您真的担心安全性,那么最好使用自己的(虚拟或非虚拟)服务器,因此危险只会来自服务器外部。

会话风险的一些示例:

  • 您的服务器在 URL 中发送会话 ID,并且您的用户点击指向 badguys.com 的链接 他们将在服务器变量中获取引用者(完整的 URL,包括您的会话 ID)、浏览器和您的用户的 IP 地址。 如果您没有检查 IP,或者您的用户使用开放代理,他们只需安装相同的浏览器版本,粘贴 URL,然后就完成了。
  • 用户转到公共电脑,登录,然后离开而不关闭他的会话(嘿,他毕竟是人类)。 下一排的人打开浏览器,检查历史记录并找到一个打开的会话。 恶心。

因此,根据我的习惯,您可以采取一些措施:

  1. 不要在 URL 中发送会话 ID; 在 PHP 中启用 session.use_only_cookies。 缺点:用户需要启用cookie。
    • 对于危险操作(更改密码、下订单...),请再次要求用户输入密码。 您也可以定期进行。 缺点:烦人。
    • 超时会话速度很快。 缺点:在大多数网站中,这会迫使用户经常登录,从而让他们烦恼。
    • 使用 SSL(避免“中间人”攻击的唯一方法)。 缺点:慢。 愚蠢的浏览器消息。 服务器上需要 SSL。
    • 检查 IP。 缺点:对于使用公共代理的访问者来说无效。 动态 IP 很烦人。
    • 检查用户代理(浏览器)。 缺点:几乎没用,UA 很容易获得,而且模仿起来也很简单。

(我想当然地认为你已经配置了 PHP 以实现最大的安全性)。

一些更极端的措施:

  • 保持服务器和浏览器之间的永久连接,例如使用 Java 小程序。 没有连接,没有会话。 缺点:用户需要 Java、ActiveX 或任何您使用的东西。 会话随浏览器关闭(这可能很好)。 不适用于非常慢的连接。 服务器负载较高。 你需要打开端口,有一个专门的小程序服务器。
  • 相同,但是使用异步请求(例如AJAX)来非常频繁地刷新会话,并且超时非常短。 或者刷新隐藏的 IFRAME。 缺点:用户需要 JavaScript。 不适用于非常慢的连接。 服务器负载较高。
  • 相同,但重新加载整个页面。 缺点:用户需要 JavaScript。 当您阅读页面时自动重新加载非常令人讨厌。

在某些特殊情况下,您可以忘记会话并使用 Apache 身份验证。 最简单的解决方案,但有很多限制。

If you are on your own server, encrypting session variables is pointless, because they don't get out of the server. See Linead answer to What do I need to store in the php session when user logged in? for more info. If you are in a shared server, you may need to encrypt every session variables, besides the session ID, because they are stored on temp files readable by the same web server all your neighbours are using.

Anyway, if you are really worried about security, you are better with your own (virtual or not) server, so danger will only come from outside your server.

Some examples of risk to your sessions:

  • Your server sends the session ID in the URL, and your user follows a link to badguys.com They will get in server variables the referer (complete URL, including your session ID), the browser and the IP address of your user. If you are not checking IPs, or your user uses an open proxy, they only have to install same browser version, paste the URL, and they're done.
  • User go to a public PC, logins, and leave without closing his session (hey, he's human after all). Next guy in the row opens the browser, check history and finds an open session. Yuck.

So, some measures you can take, by my usual preference:

  1. Don't send the session ID in the URL; enable session.use_only_cookies in PHP. Cons: User needs to enable cookies.
    • On dangerous actions (change password, make an order...), ask user for password again. You can do it periodically too. Cons: Annoying.
    • Timeout sessions fast. Cons: In most sites, this will force users to login often, annoying them.
    • Use SSL (only way to avoid 'man in the middle' attacks). Cons: Slow. Stupid browser messages. Need SSL on server.
    • Check the IP. Cons: Inneffective for visitors using a public proxy. Annoying for dynamic IPs.
    • Check the User Agent (browser). Cons: pretty much useless, UA is easy to get and trivial to imitate.

(I take for granted you have yet PHP configured for maximum security).

Some more extreme measures:

  • Maintain a permanent connection between server and browser, e.g. using a Java applet. No connection, no session. Cons: User needs Java, ActiveX or whatever you use. Session closes with browser (this can be good). Doesn't work on very slow connections. Higher load on server. You need to open ports, have a special server for the applet.
  • The same, but using asynchronous requests (e.g. AJAX) to refresh very frequently the session, and a very short timeout. Or refreshing a hidden IFRAME. Cons: User needs JavaScript. Doesn't work on very slow connections. Higher load on server.
  • The same, but reloading the whole page. Cons: User needs JavaScript. An automatic reload while you are reading a page is very annoying.

In some corner cases, you can forget about sessions and use Apache authentication instead. Simplest solution, but a lot of limitations.

最佳男配角 2024-07-21 18:53:36

由于指纹存储在服务器端,因此您不需要使用加盐哈希。 “正常”哈希足以减少数据。

As the fingerprint is stored on the server side, you don’t need to use a salted hash. A “normal” hash is enough to reduce the data.

鹿童谣 2024-07-21 18:53:36

我看到给你的指纹加盐的目的之一。 如果坏人掌握了您的会话数据库(天知道为什么)但没有掌握您的代码,他就无法通过尝试常见的用户代理来“猜测”您的指纹识别方法。

I see one purpose in salting your fingerprint. If a bad guy gets hold of your session-db (god knows why) but not of your code he couldnt "guess" your fingerprinting method by trying the common user-agents against it.

花桑 2024-07-21 18:53:36

我这样做也是为了部分防止会话模拟攻击。 您还需要包含 IP 地址。

请记住,当客户端的浏览器自动更新时,用户代理会发生变化,您会认为他的会话已被劫持;)

I do that as well to partially protect from session impersonation attacks. You need to include the IP address as well.

Keep in mind that when the client's browser auto updates the user agent changes and you'll think that his session has been hijacked ;)

以可爱出名 2024-07-21 18:53:36

请记住,如果您这样做,您将迫使人们在升级浏览器后重新登录。 这可能没问题,但只要确保这是您的意图即可。

使用用户的远程地址也并非没有问题。 许多人在不同地点使用同一台计算机。 移动设备、在家和工作中使用的笔记本电脑、在 Wifi 热点中使用的笔记本电脑等。 恕我直言,以新的 IP 地址需要登录的方式使用 IP 地址是一个坏主意除非您正在处理高度敏感的信息(例如网上银行)。 是这样吗?

你担心什么? 外部攻击? 或者在共享主机的情况下,有人可以读取您的会话信息?

如果是后者,解决方案很简单:只要不在会话中存储任何敏感内容即可。 任何敏感内容都应该存储在数据库中。

在创造秘密盐方面,你需要使用一些无法猜测的东西。 我会选择像创建用户时创建的随机字符串之类的东西。 如有必要,每次会话失效时都重新创建它。

至于如何使其更安全,您自己也说过:用户代理字符串是有限的(少于一百个可能会覆盖 99.99% 的用户)。 盐只会增加可能性的数量。 话虽这么说,如果您在所有会话中使用相同的盐,那么用暴力发现它只是时间问题。

Bear in mind that if you do that you're forcing people to login again if they upgrade their browser. This can be OK but just make sure it's your intent.

Using the user's remote address is not without problems either. Many people use the same computer from different locations. Mobile devices, laptops being used at home and work, laptops being used at Wifi hotspots and so on. IMHO it's a bad idea to use IP address in such a way that a new IP address requires a login unless you're dealing with highly sensitive information such as online banking. Is that the case?

What are you concerned about? External attack? Or in a shared host situation that someone can read your session information?

If it's the latter, the solution is simple: just don't store anything sensitive in the session. Anything sensitive should be stored in the database.

In terms of creating a secret salt, you need to use something that isn't guessable. I would go for something like a random string that's created when the user is created. If necessary recreate it each time the session is invalidated.

As for what it would make it more secure, you said it yourself: there are limited user agent strings (less than a hundred will probably cover 99.99% of users). A salt simply increases the number of possibilities. That being said, if you use the same salt for all sessions then it's only a matter of time before it's found with brute force.

铃予 2024-07-21 18:53:36

好的,例如,我正在使用以下虚构代码:

<?php

// The sessionid cookie is now a certain hash
if ( array_key_exists ( $_COOKIE [ 'sessionid' ] ) )
{
    // Get the session from database
    $db_sessid = $pdo -> getStuff ( 'session_database', $_COOKIE [ 'sessionid' ] );

    if ( $db_sessid !== null && $db_sessid [ 'fingerprint' ] == sha1 ( 'SOMESALT' . $_SERVER [ 'HTTP_USER_AGENT' ] ) )
    {
        set_cookie ( ... ); // New sessionid and write also to DB

        // User is now logged in, execute some user stuff
    }
    else
    {
        // Session doesn't exist, or the fingerprint does not match
    }
}

现在攻击者仍然只需要 sessionid,它位于 cookie(随 HTTP 标头发送)和 useragent 中。 那么额外的盐还有什么意义呢?

在我看来,检查 IP 也不是一个好的选择,一些提供商或代理会在每个请求时更改它们。

到目前为止谢谢(-:

Okay, for example I'm using the following fictional code:

<?php

// The sessionid cookie is now a certain hash
if ( array_key_exists ( $_COOKIE [ 'sessionid' ] ) )
{
    // Get the session from database
    $db_sessid = $pdo -> getStuff ( 'session_database', $_COOKIE [ 'sessionid' ] );

    if ( $db_sessid !== null && $db_sessid [ 'fingerprint' ] == sha1 ( 'SOMESALT' . $_SERVER [ 'HTTP_USER_AGENT' ] ) )
    {
        set_cookie ( ... ); // New sessionid and write also to DB

        // User is now logged in, execute some user stuff
    }
    else
    {
        // Session doesn't exist, or the fingerprint does not match
    }
}

Now the attacker only still needs the sessionid, which is in the cookie (sent along HTTP headers) and the useragent. So what's still the point of the additional salt?

Checking for IP's is also in my opinion not such a good option, some providers or proxy's change them every single request.

Thanks so far (-:

送君千里 2024-07-21 18:53:36

在满足所有安全参数后,您允许 cookie 只设置一个 cookie,如果不满足参数,则 cokkie 永远不会被设置好,但如果 cookie 有一个可见的参数,那么会发生什么。 同样,如果条件从未得到满足,则会话也将不会得到满足。这就是您真正想要的吗? 请记住检查满足条件通过 cookie 提供会话和会话数据方式
记住 cokkie 位于客户端浏览器上
问候斯泰利奥斯

you allow the cookie after all the safegard parameters are met to just set a cookie if parameter are not met the cokkie will never be set nice but if the cookie has a parameter vissible what happens then. as well if conditions are never met the session willneevr be met.is that what you realy want. remeber a check met conditions give session and seession data way through the cookie
remember the cokkie sits on the clients browzer
regards stelios

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文