News:

The anti-spam plugins have stopped being effective. Registration is back to requiring approval. After registering, you must ALSO email me with your username, so that I can manually approve your account.

Main Menu

Sessionless Secure Cookies

Started by Databits, May 07, 2009, 07:22:59 PM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

Databits

Not sure how many people here actually do programming, but this technique is applicable to all web languages. I'll be focusing on PHP in this particular example though.

When writing website systems, there's generally a few rules of thumb that you want to always follow. First, you want your users data to be secure and second, especially on high traffic sites, try to offload anything you safely can onto the users browser.

Now, some people who have used things like PHP sessions are probably using it wrong. First and foremost, with ANY session system, it's usually horrible practice to store a ton of information in the session. Generally, you want to store only id's and use that information on your backend to reference the data. Never, ever, ever store the data directly in the session. Simply put, when you start relying on doing such a thing it can end up giving you unintended consequences later down the road.

The easiest example I think I can give for this would be something like user profile information. Say after you log in a user, you decide that to keep things easy you'll just store all their profile information into their session. In things like PHP the session data is read/written to the disk (or database) each and every page load, sometimes multiple times per page load depending on how you write your system. Not only can this kill your sites performance, when you start getting more and more data it can also cause unexpected issues. For instance, one day you decide to change the session handling to store the information in a database rather than the file system, but you start getting errors because information is being truncated off and corrupting the session data itself.

The simplest way to avoid something like that is simply don't do it. It's bad practice anyhow.

Now, another nice trick, is storing the session information on the users browser in a cookie. This gives you a few advantages. Not only does it significantly simplify your session handling, it makes it so you no longer need to store/retrieve this information from a database or file system. This is best done when you are actually properly using sessions in the first place (rather than storing all the users information, store just the user id and use that to reference the users information later only when you actually need it). However, you can't just store something like a user id and hope that you won't have malicious users spoofing information.

But there is a way, and it's actually quite simple. By using something like a server-side secret value (usually something extremely long and extremely random), and hashing functions you can safely place such a thing into a cookie and make it impossible to "easily" crack (by that I mean, no encryption in the world is 100% totally unbreakable).

First of all, you'll need a non-changing secret key in your application. Something which you never, ever, distribute out to anyone (it'd break any security you add to do so). For this example, I'll be using a random SHA1 hash, but you'd likely use something a little stronger.

Secret: 2bffd564c0979f5d4bf68ab630c5234fb38b3493

Now, say you have a user log into your site. We'll say that this particular user's id is 54.

Now here's where the magic comes in, you need to generate a cookie value using your secret value, the users id, and some random seed value that you'll generate on the spot now. So lets generate our seed:

Seed: 9389552

So we now have a seed (randomly generated for each new cookie), a user id, and a secret key. Lets make a cookie (for this demonstration I'll be using SHA1 one-way encryption, you may substitute this with any other method you wish, such as SHA256).

The basic formula for this is:

UserId + '-' Seed + '-' + SHA1(UserId + Seed + Secret)

which for this case would end up being:

54-9389552-5fd41536bfb364e6fec7922297f041f13445eea8

This here is the value you would store in the users value within your cookie. But this is only the first half of it. You'll need to read and validate the cookie too. So here we go (I assume people know how to read/write cookies who have gotten to this point).

First, if the cookie is set, read in the value. In this case the value of our cookie was "54-9389552-5fd41536bfb364e6fec7922297f041f13445eea8".

Now, split the cookie up so you have the user id, seed, and generated sha1 value:
uid = 54
sed = 9389552
hsh = 5fd41536bfb364e6fec7922297f041f13445eea8

At this point you have all you need to make sure that this cookie is valid to allow access for user 54 to the user. Simply compare the hash with another hash generated using the same method above:

If 5fd41536bfb364e6fec7922297f041f13445eea8 EQUALS SHA1(uid+sed+Secret)

then your id is valid, otherwise they're spoofing and you can handle it however you want to. ;)

(\_/)    ~Relakuyae D'Selemae
(o.O)    
(")_(")  [Libre Office] [Chrome]

Xepher

Nifty explanation there... pretty much exactly what the linux system does when storing passwords in the shadow file, but instead of a server-secret, it's the user's password. What you said about offloading stuff to the browser/client though... when you put it like this, it makes it obvious how you can forgo PHP session files/backend entirely, and eliminate that (usually) pointless disk I/O. Definitely have to keep that in mind if/when I ever do a large project.

Databits

We're actually about to do this for our main ecommerce software. We're finding that even with the standard PHP session shit, the disk i/o is really starting to kill performance. If we eliminate the session stuff it should help a hell of a lot in load.
(\_/)    ~Relakuyae D'Selemae
(o.O)    
(")_(")  [Libre Office] [Chrome]

yny-u

#3
*Edit* I am mostly talking about what might happen if for some reason you decided to store more than just a user ID in the cookie, which is not really what Databits is talking about doing above. Just a clarification. *Edit*

Especially useful for semi-public authenticated applications (forums and such) where you are dealing with a lot of users, and the damage from somebody breaking into the system would be comparatively small, or if you aren't actually storing really sensitive stuff in the cookie. You can be clever about what how you use user data, and which data you store... For example, a credit card number might not need to be stored efficiently.. You might only have to check it once.

I guess this, like most other security questions, boils down to a trade-off between security and usability/efficiency.

If you are dealing with very sensitive information–say credit card or bank numbers–this might not be such a good idea. Unix systems used to store passwords in a similar way (in a public file, but hashed–back before they started using the shadow file), but one of my teachers recently had us crack a bunch as homework. Somebody will at some point crack MD5, especially as there is probably a lot of money to be made in doing so (legitimately or otherwise). Of course no system is really secure, but at least if you are only storing a random token in the cookie, you are not exposing any data in semi-public fashion (even if it is encrypted). Obviously, if someone breaks into your database they can still get the data, but as long as you are only storing hashes in your database, at least two major things (rather than just your encryption) need to fail before any real data is exposed.

Anyway, I just thought I should emphasize that all solutions are ultimately a trade-off, and you need to decide on a case-by-case basis.

Thanks for the tip Databits. Any way of avoiding disk I/O is good ^^

Databits

Actually, my primary professions deals with eCommerce applications. Remember some of the first points of the post. It's plain bad practice to really store a lot of stuff in a session. For this example I was just storing a user id. You'd retain all the user information server-side and never store such things in a cookie, regardless of encryption.

The idea of using just the user id is simply to verify something like a logged in user. You'd only actually pull the data for that user when it's actually needed. As for credit card stuff, it's generally not good practice to ever store credit card information in full without some sort of higher security anyhow. There's actually a rather specific set of information for PCI compliance that your application must follow when using credit cards. Now, even if you do happen to store such things, you generally don't show it to the user anyhow (especially over a non-SSL connection).

MD5 has been cracked already actually. It's a small enough hash that you can break it with today's hardware due to databases being able to easily hold every possible value and reference it rather easily with a rather simplistic dictionary attack. Your encryption is only as strong as the method you're using. For instance, SHA1 will likely be broken before SHA256 is, which will be broken before SHA512, which will be broken before other even higher encryption methods. Unix shadow files are an extremely small and rather easy to break hash much like MD5 (hell it's smaller isn't it?), so they weren't a strong hash to begin with.

Honestly, this method here is no less secure than just brute forcing something like a PHP session id. If anything, when using a higher encryption method it'd be more secure, because even if they have the user id (so long as your application itself isn't broken) there's nothing they can do with it unless they manage to obtain your secret key for your application. Only at that point is your security really compromised.

As one of my instructors once put it, if you really want your data secure, take your system offline, lock it in a safe, and drop it to the deepest part of the ocean. :P
(\_/)    ~Relakuyae D'Selemae
(o.O)    
(")_(")  [Libre Office] [Chrome]

yny-u

#5
Hahah, yes. I was tempted to mention the unplug/power-down/smash security option.

But seriously.
The point I was trying to make is that if you expose *more* than just the user id, you need to be careful.

And anyway, no matter how random your cookie is, someone can always just hijack the session or something, so any small boost in security (if any) you might get from using sessions doesn't really help much... Which is what you are saying, correct?

Thanks again.

Databits

More or less. The thing is the more complex your session cookie is, the lower the chances someone will manage to hijack it anyhow. Again, storing anything other than simple ids in a session is bad practice and wasteful anyhow. Even if using built-in PHP sessions or something similar. Because it loads that data every single time, regardless if you actually need it or not, which is a waste of resources. Only load data when you actually need it for something. ;)
(\_/)    ~Relakuyae D'Selemae
(o.O)    
(")_(")  [Libre Office] [Chrome]