Tips on signed cookies instead of sessions

2019-01-30 05:41发布

问题:

I'm considering ditching PHP's $_SESSION (i.e. the server-side session handling, to add some language-agnostic flavor) and using signed cookies instead, since I've heard so much good about them (Flickr uses them, so they ought to be good enough for me too).

I understand the basic context of the technique: Use cookies freely to pass key-value pairs from client to server, and sign them to make sure that the values aren't tampered with.

But what would be a good way to implement the signing part? Also; since the traffic will probably be HTTP, is there a good way to send sensitive data (such as a user's password) with this method, while working against cookie-stealing and/or tampering?

回答1:

Why bother?

I wouldn't use this technique for sensitive data. It can be useful in combination with a regular session though - you can give the client a cookie with a normal session id, but also include all those key/value pairs that your application needs on every page. This way, you can avoid hitting your session storage for every page request.

You should aim to keep the amount of data pretty tight, since it will be sent with every request.

With that in mind, onwards...

Signing data with a hash

If the data isn't sensitive, you can sign the values with sha1 hash made from a combination of the key/value pairs and a shared secret. e.g.

$values=array(
  'user_id'=>1,
  'foo'=>'bar'
);
$secret='MySecretSalt';

$plain="";
foreach($values as $key=>$value)
{
    $plain.=$key.'|'.$value.'|';
}
$plain.=$secret;
$hash=sha1($plain);

Now give the client a cookie with all the values and the hash. You can check the hash when the cookie is presented. If the hash you calculate from values presented by the client doesn't match the expected hash, you know the values have been tampered with.

Encrypting sensitive data

For sensitive data, you'll need to encrypt the values. Check out the mcrypt extension which offers a lot of cryptographic functions.

Cookie theft

With regards to cookie stealing, if you're putting user credentials into a cookie and trusting it, then someone who obtains that cookie can impersonate that user until the password is changed. A good practice is to remember how you authenticated a user, and only grant certain privileges if the user explicitly logged in. For example, for a forum you might let someone post, but not change their account details like email address.

There are other techniques for "autologin" cookies, involving giving such cookies a token value which you only allow to be used once. Here's a good article on that technique.

You could also look at including the client IP in a signed cookie, and if it doesn't match the IP presenting the cookie, you get them to log in again. This provides more protection, but won't work for people whose apparent IP address keeps changing. You could make it an optional feature, and give the user a way to opt out. Just an idle thought, I've not seen that done in practice :)

For a nice article which explains session theft, hijack and fixation see Sessions and Cookies which offers a few more techniques to try, such as using the User-Agent header as an additional signature.



回答2:

I made CookieStorage exactly for this purpose. All stored values are securely signed with your private key via RIPEMD160 hashing (and salted with time), and optionally encrypted with RIJNDAEL256.

Each value is stored with the timestamp, which is retrievable.

Signed example.
Encrypted example.

If you prefer, you can use the hash/encrypt/decrypt functions of your choice.



回答3:

Signed cookies in PHP

The other answers to this question are a bit outdated. PHP 5.2 added the httponly parameter to the setcookie function, effectively adding native signed cookie support. According to the setcookie function httponly parameter documentation:

"When [set to] TRUE the cookie will be made accessible only through the HTTP protocol. This means that the cookie won't be accessible by scripting languages, such as JavaScript. It has been suggested that this setting can effectively help to reduce identity theft through XSS attacks (although it is not supported by all browsers), but that claim is often disputed"

Setting this parameter to true will also disable the ability to edit this cookie using other browser-based tools, such as Chrome's DevTools. To make the signed cookie even more secure I highly recommend narrowing down the path or domain it uses. You can specify those using the path and domain parameters. And of course it never hurts to secure the cookie using the secure parameter if your website is being loaded over HTTPS. The result will be a line like this:

setcookie('signedCookie','uneditable value here', 0, '/', 'www.example.com', TRUE , TRUE);

Why use signed cookies?

There are, in fact, specific situations in which signed cookies are helpful while other methods, such as a session, are not. For example, let's assume the website/application in question uses a load balancer to improve performance. Now let's also assume that that loads balancer has multiple physical servers from which files are being served and a sticky session option is not available. In such a situation signed cookies are virtually the only secure way to preserve state in PHP.