对 cookie 进行编码,使其无法被欺骗或读取等

发布于 2024-10-17 16:50:48 字数 205 浏览 7 评论 0原文

我正在编写一个 PHP 应用程序。我想将用户登录信息存储在 cookie 中,这样用户就不必在每次访问时都登录。

我想对它们进行编码或混淆,以便它们无法被读取或篡改。

最好的方法是什么?

更新:

我不会在 cookie 中存储密码,只是存储一个用户 ID,以便我知道他们是谁,但我希望对其进行编码或加密,这样就没有人可以欺骗其他用户

I am writing a PHP application. I want to store user login information in cookies so user's dont have to log in on every visit.

I want to encode them or obfuscate them so that they cannot be read or tampered with.

What is the best way to do this?

Update:

I am not going to be storing passwords in the cookies, simply a user ID so that I know who they are, but I want this to be encoded or encrypted so no one can spoof other users

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

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

发布评论

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

评论(5

长安忆 2024-10-24 16:50:48

简短的回答

是不要这样做。从长远来看你会后悔的。当然,您可以对其进行加密,但是当有人找出您的加密密钥时会发生什么。现在,您只需将每个人的凭据放在盘子上交给他们(好吧,不是真的,但足够接近)。

更好的方法

为什么不创建一个随机令牌并将其与用户名一起存储,而不是加密存储用户名和密码?您想要相当大的东西,所以像 sha256 哈希这样的东西就足够了。

$randomToken = hash('sha256',uniq_id(mt_rand(), true).uniq_id(mt_rand(), true));

然后,将其与用户一起存储在数据库中,并将 cookie 发送到客户端(我还建议对令牌进行签名以防止篡改:

$randomToken .= ':'.hash_hmac('md5', $randomToken, $serverKey);

现在,当您验证时,首先检查哈希是否匹配:

list($token, $hmac) = explode(':', $_COOKIE['remember_me'], 2);
if ($hmac != hash_hmac('md5', $token, $serverKey)) {
    die('tampered token!');
}

从那里,只需通过令牌查找用户,如果找到,请登录该用户。

我还建议在每次更改密码时更改令牌。

要直接回答您的问题

注意:不要在实时情况下执行此操作,生产代码。因此,不要像这样公开您的用户信息。但是,我确实添加了一些额外的检查(例如签署 cookie)。 )以使其更安全,但您已被警告...

要对其进行编码,我将使用 mcrypt 将数据加密到 cookie 中。然后,我会制作一个随机盐并将其与用户行一起存储,然后使用 hash_hmac 使用这种独特的盐。这样,如果有人拦截 cookie并找出加密密钥,您仍然可以检测到无效的 hmac,从而可以发现篡改。

function generateCredentialsCookie($user_id, $password) {
    $encrypted = encrypt($user_id.':'.$password, $secretkey);
    $salt = uniq_id(mt_rand(), true);
    $encrypted .= ':'.hash_hmac('sha256', $encrypted, $salt);
    storeSaltForUser($user_id, $salt);
    set_cookie('credentials', $encrypted);
}

function readCredentialsCookie() {
    $parts = explode(':', $_COOKIE['credentials']);
    $salt = array_pop($parts);
    $encrypted = implode(':', $parts); //needed incase mcrypt added `:`
    $raw = decrypt($encrypted, $secretkey);
    list ($user_id, $password) = explode(':', $raw, 2);
    if ($salt == getSaltForUser($user_id)) 
        return array($user_id, $password);
    } else {
        return die('Invalid Cookie Found');
    }
}

注意 - 这是伪代码。为了确保安全,您将需要更多信息(例如检查无效值,确保其解密成功等)。

不要使用长时间运行的会话!

您应该将会话过期时间保持在尽可能低的水平(我通常使用 30 分钟的会话,但有些网站会更低)。过期时间是在最后一次使用之后,因此只要该网站正在被积极使用,就没有关系。

至于为什么不使用长时间运行的会话,这里有一些缺点:

  • DOS(创建拒绝服务漏洞

    • 磁盘空间 - 每个会话使用相当少量的磁盘空间。但是,当您进行长时间运行的会话时,每个新会话只会添加到先前的总数中。因此,对于长时间运行的会话,有人只需要使用新的会话 ID 一遍又一遍地访问您的网站,然后突然间您的磁盘空间就不足了(假设磁盘空间正常)。

    • 文件夹空间 - 每个会话在一个文件夹中占用一个文件。大多数流行的文件系统在单个文件夹中包含大量文件时会减慢速度。所以如果你放入100万个会话文件,读取或写入一个会话文件将会很慢(非常慢)。而且垃圾收集(清理旧文件)将非常非常非常慢(如果它真的会运行的话)。

  • 会话劫持漏洞已被公开。这是因为您在网站上打开的会话越多,就越容易猜测有效标识符(感谢

可能还有其他的,但这只是一个快速概述。如上所述,不要使用长时间运行的会话,而是使用签名的记住我令牌。你会过得更好,也更安全......

The short answer

Don't do it. You'll regret it in the long run. Sure, you could encrypt it, but what happens when someone figures out your encryption key. Now you just handed everyones credentials to them on a plate (well, not really, but close enough).

A Better Way Of Doing It

Instead of storing the user-name and password encrypted, why not create a random token and store that with the username? You'd want something sizable, so something like a sha256 hash should suffice.

$randomToken = hash('sha256',uniq_id(mt_rand(), true).uniq_id(mt_rand(), true));

Then, store it in the db along side the user, and send in a cookie to the client (I'd also suggest signing the token as well to prevent tampering:

$randomToken .= ':'.hash_hmac('md5', $randomToken, $serverKey);

Now, when you verify, first check that the hash matches:

list($token, $hmac) = explode(':', $_COOKIE['remember_me'], 2);
if ($hmac != hash_hmac('md5', $token, $serverKey)) {
    die('tampered token!');
}

From there, just lookup the user by the token. If you find one, log that user in.

I'd also suggest changing the token on every single password change.

To answer your question directly

Note: do not do this in live, production code. You can never fully trust data that leaves your web-server. So don't expose your user's info like that. It's not worth it. However, I did add some additional checks (such as signing the cookie) to make it somewhat safer, but you have been warned...

To encode it, I would use mcrypt to encrypt the data into the cookie. Then, I would make a random salt and store it with the user row, and then sign the encrypted data with hash_hmac using that unique salt. That way, if someone intercepts the cookie and figures out the key to crypt, you can still detect the invalid hmac, so you can find tampers.

function generateCredentialsCookie($user_id, $password) {
    $encrypted = encrypt($user_id.':'.$password, $secretkey);
    $salt = uniq_id(mt_rand(), true);
    $encrypted .= ':'.hash_hmac('sha256', $encrypted, $salt);
    storeSaltForUser($user_id, $salt);
    set_cookie('credentials', $encrypted);
}

function readCredentialsCookie() {
    $parts = explode(':', $_COOKIE['credentials']);
    $salt = array_pop($parts);
    $encrypted = implode(':', $parts); //needed incase mcrypt added `:`
    $raw = decrypt($encrypted, $secretkey);
    list ($user_id, $password) = explode(':', $raw, 2);
    if ($salt == getSaltForUser($user_id)) 
        return array($user_id, $password);
    } else {
        return die('Invalid Cookie Found');
    }
}

Note - that's pseudo-code. You'll need much more in there to be secure (such as checking for invalid values, making sure it decrypts successfully, etc)..

Do NOT Use Long-Running Sessions!

You should keep your session expiration as low as practical (I typically use 30 minute sessions, but some sites are lower). The expire time is after the last usage, so as long as the site is being used actively it won't matter.

As far as why not to use a long running session, here are some cons:

  • DOS (Denial Of Service vulnerabilities are created

    • Disk space - Each session uses a reasonably small amount of disk space. But when you have a long running session, each new session only adds to the prior total. So with long-running sessions someone just needs to keep visiting your site over and over with a new session id and all of a sudden you're out of disk-space (assuming a sane disk).

    • Folder space - Each session takes one file in one folder. Most popular filesystems will slow down with a large number of files in a single folder. So if you put 1 million session files, reading or writing to a session file will be slow (very slow). And garbage collection (which cleans old files) will be VERY VERY VERY slow (if it'll even run at all).

  • Session Hijacking vulnerabilities are opened up. This is because the more sessions you have open on the site, the easier it will be to guess a valid identifier (thanks to the birthday attack). The fewer sessions you have laying around, the harder it will be to guess a valid one.

There are likely others, but that's a quick overview. Instead of long-running sessions, use a signed remember-me token as described above. You'll be far better off, and far more secure...

暗喜 2024-10-24 16:50:48

将用户数据存储在 cookie 中是错误的方式。您应该使用 PHP 会话。客户端上唯一存储的内容是简单 cookie 中的会话 ID(PHP 将自动为您处理)。它已经是一个加密的字符串。

PHP 将在服务器端跟踪用户数据。

这将实现您所描述的所需效果,同时比使用 cookie 更安全。

Storing user data in a cookie is the wrong way. You should use PHP Sessions. The only thing that will be stored on the client will be the session id in a simple cookie (that PHP will handle for you automatically). It's already an encrypted string.

PHP will keep track of the user data on the server side.

This will accomplish the desired effect you've described, while remaining more secure than using cookies.

寻找我们的幸福 2024-10-24 16:50:48

绝对在任何情况下都不应该将用户凭据存储在 cookie 中。您应该始终假设 cookie 与所有用户输入一样都是不可信的。

有几种方法:

如果用户选择“记住我”,则只需在初始化会话之前设置以下内容即可。

session_set_cookie_params(60 * 60 * 24 * 7); //save cookie session for 7 days

否则,在数据库中存储一个唯一的令牌,并查询该令牌以查看它是否属于用户,如果是,则填充他们的会话。

不过我会推荐第一个选项。

Absolutely in no circumstances should you ever store user credentials in the cookie. You should always assume cookies, as all user input, cannot be trusted.

A couple methods:

If the user chooses "remember me", then simply set the following prior to initializing your session.

session_set_cookie_params(60 * 60 * 24 * 7); //save cookie session for 7 days

Otherwise, store a unique token in the database, and query that token to see if it belongs to a user, and if so, populate their session.

I would recommend the first option though.

始终不够 2024-10-24 16:50:48

保存前给饼干加盐。根据需要添加用户代理、ip 以扩展安全性。

$cookie_to_save = sha1($pwd . $user_agent . $yourSalt);

自动登录时

if($cookie_saved == sha1($pwd . $user_agent . $yourSalt) ok...

Salt cookie before saving. Add user-agent, ip as you wish to extend security.

$cookie_to_save = sha1($pwd . $user_agent . $yourSalt);

upon autologin

if($cookie_saved == sha1($pwd . $user_agent . $yourSalt) ok...
空气里的味道 2024-10-24 16:50:48

生成 16 或更长的加密安全随机数并将其存储在 cookie 中会更简单。无需加密/解密。
使用它时在服务器端检查有限的有效时间(不要依赖 MaxAge 或 Expires)。这是一个会话 cookie。

这是非常安全的,因为没有关键信息离开服务器。

此技术的缺点是您需要为该会话 ID 创建数据库索引并为每个请求进行数据库查找。只要延迟不明显且服务器负载可接受,这对于大多数网站来说可能就足够了。

为了最大限度地减少服务器负载和响应延迟,将用户 ID、用户角色以及 cookie 创建标记存储在安全 cookie 值中是一个有效的选择。不需要数据库或缓存命中来清除无效请求,并且如果用户 ID 很紧凑,则索引将很小并且查找速度更快。但是,除非您知道自己在做什么,否则不要发明自己的安全值编码。

这种方法的缺点是如果有人发现了密钥,他就可以伪造cookie。然后,他可能会使用不同的用户 ID 创建 cookie 并访问他们的帐户。因此,在风险有限且对密钥的访问得到良好控制的情况下使用此方法。执行加密/解密并隐藏密钥的专用硬件设备可能是一种解决方案。但目前这种情况并不常见且昂贵。它的延迟可能比在缓存中查找随机会话 ID 还要大。后者是更简单、更安全的解决方案。

使用公钥/私钥来保护 cookie 中的值,可用于保护私钥。但要做到这一点要复杂得多,而且延迟可能与具有简单随机缓存会话 ID 的系统相当。

另请注意,任何使用 cookie 的方法都需要使用 HTTPS。否则,中间人或监视交换数据的人可以轻松获取 cookie 的副本并冒充用户。

It is simpler to generate a 16 or longer cryptographically secure random number and store this in the cookie. No need for ciphering/deciphering.
Use it with a limited validity time checked on the server side (don't rely on MaxAge or Expires). This is a session cookie.

This is very secure since no critical information leaves the server.

The disadvantage of this technique is that you need to create a database index for that session Id and do a database lookup for every request. That might be good enough for most web site as long as the latency is not perceptible and the server loads acceptable.

To minimize server load and response latency, storing the user ID, his role, and the cookie creation stamp inside a secured cookie value is a valid option. No database or cache hit are required to weed out invalid requests and if the user id is compact the index will be small and faster to lookup. But don't invent your own secure value encoding unless you know what you are doing.

The disadvantage of this method is that if someone finds out the key, he can forge cookies. He may then create cookies with different user ids and access their account. So use this method where the risk is limited and access to the key well controled. A dedicated hardware device performing the cipherment/decipherment and concealing the key might be a solution. But that is currently uncommon and expensive. It's latency might be bigger than looking up a random session id in a cache. The later is the simpler and safer solution.

Using public/private keys to secure the value in the cookie, could be used to secure the private key. But that is much more complex to get right, and the latency might be comparable to a system with a simple random cached session id.

Note also that any method using cookies require to use HTTPS. Otherwise a man in the middle or someone spying the data exchanged could easily get a copy of the cookie and impersonate the user.

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