36

In an effort to increase performance, I was thinking of trying to eliminate a plain 'session cookie', but encrypt all the information in the cookie itself.

A very simple example:

userid= 12345
time=now()
signature = hmac('SHA1',userid + ":" + time, secret);

cookie = userid + ':' + time + ':' + signature;

The time would be used for a maximum expirytime, so cookies won't live on forever.

Now for the big question: is this a bad idea?

Am I better off using AES256 instead? In my case the data is not confidential, but it must not be changed under any circumstances.

EDIT

After some good critique and comments, I'd like to add this:

  • The 'secret' would be unique per-user and unpredictable (random string + user id ?)
  • The cookie will expire automatically (this is done based on the time value + a certain amount of seconds).
  • If a user changes their password, (or perhaps even logs out?) the secret should change.

A last note: I'm trying come up with solutions to decrease database load. This is only one of the solutions I'm investigating, but it's kind of my favourite. The main reason is that I don't have to look into other storage mechanism better suited for this kind of data (memcache, nosql) and it makes the web application a bit more 'stateless'.

10 years later edit

JWT is now a thing.

Evert
  • 93,428
  • 18
  • 118
  • 189
  • 1
    Hi Rice Flour Cookies, it's PHP, but I left it out to avoid a platform-specific discussion. Currently we're using PHP on multiple webservers, and a Master-Master mysql setup. Session cookies are cached in memcached. Our bottleneck is mostly in addition and removal of cookies. Replication is additional also going to a secondary datacenter, and Session-related queries is a big chunk of the traffic. – Evert Jul 13 '10 at 19:49
  • Evert, I would be interested in if you successfully implemented entire client-side sessions with the approach outlined in your question. What are the details of your implementation to make your system as secure as possible? Thanks :) – Dr. Jan-Philip Gehrcke Jul 11 '11 at 08:42
  • Hi Jan, after this question I summarized all this in a blog post: http://www.rooftopsolutions.nl/blog/storing-encrypted-session-information-in-a-cookie – Evert Jul 12 '11 at 00:16
  • 3
    _The 'secret' would be unique per-user and unpredictable (random string + user id ?)_ <-- and where are you going to store that unique secret or unique random string? Oh yeah. The database. So you'll end up with the exact same amount of strain on the database every time you pull the random string, to be able to do that signature, to validate the user. It's the same amount of strain as pulling a normal cookie validation from the database. (Which is what you were trying to avoid in the first place) ): – cake Apr 26 '17 at 19:33
  • 2
    same thing @ _If a user changes their password, (or perhaps even logs out?) the secret should change._ <-- where are you going to store a different secret, per user? ..... same place you were trying to avoid putting pressure on, in the first place ._. – cake Apr 26 '17 at 19:34
  • 2
    This is an old question, but this approach to using signed cookies has now become mainstream. The python framework Flask uses this for its sessions (http://flask.pocoo.org/docs/dev/api/#sessions). Although a single secret is typically used application wide, not a secret per user. – Michael Ekoka Sep 10 '17 at 20:21
  • 1
    @cake Was thinking the same, he stored the state anyhow. – Suraj Jain Mar 05 '20 at 09:49

5 Answers5

30

A signed token is a good method for anything where you want to issue a token and then, when it is returned, be able to verify that you issued the token, without having to store any data on the server side. This is good for features like:

  • time-limited-account-login;
  • password-resetting;
  • anti-XSRF forms;
  • time-limited-form-submission (anti-spam).

It's not in itself a replacement for a session cookie, but if it can eliminate the need for any session storage at all that's probably a good thing, even if the performance difference isn't going to be huge.

HMAC is one reasonable way of generating a signed token. It's not going to be the fastest; you may be able to get away with a simple hash if you know about and can avoid extension attacks. I'll leave you to decide whether that's worth the risk for you.

I'm assuming that hmac() in whatever language it is you're using has been set up to use a suitable server-side secret key, without which you can't have a secure signed token. This secret must be strong and well-protected if you are to base your whole authentication system around it. If you have to change it, everyone gets logged out.

For login and password-resetting purposes you may want to add an extra factor to the token, a password generation number. You can re-use the salt of the hashed password in the database for this if you like. The idea is that when the user changes passwords it should invalidate any issued tokens (except for the cookie on the browser doing the password change, which gets replaced with a re-issued one). Otherwise, a user discovering their account has been compromised cannot lock other parties out.

bobince
  • 528,062
  • 107
  • 651
  • 834
  • 1
    reading up about this it would seem a good idea to; mark the cookie as secure, set a expire time, bind it to one area of your site (e.g. no user account change commands). also hash the user agent string and ip address into the secret so cookie cannot be stolen. be aware of less than 4k cookie max size limit. there are so many gotacha i think its wiser to look for a faster serverside session impl (riak, memcached) than risking a security bug with this. – simbo1905 Apr 28 '14 at 05:57
  • @simbo1905 - thanks for an excellent summary of details that must be gotten right. Re "so many gotchas" - are there any more factors than the ones you mention? Sounds perfect for "remember me" up to 24 hours from a personal device. [Unlike the Q, I would combine this with comparing to a token stored on server.] – ToolmakerSteve Apr 18 '19 at 10:54
  • if you are happy to do remember-me then you are already comfortable with some things. you might tell users not to do it on public or shared machines but mistakes will happen. So remember-me is not for sites that need more than minimal security. Adding it today when your don't need security then adding more features tomorrow that need better security and forgetting to disable remember-me or reauthenticate users to do risky actions (like changing that email) is a possible future gotcha. Implementing your own security features is notoriously hazardous. wiser to use a library or free teir auth0 – simbo1905 Apr 18 '19 at 11:31
  • @simbo1905 - thanks. I have no desire to re-invent the wheel - I see I need to do more background research on this topic. Can you recommend links to discussions/reviews/comparisons of already-implemented solutions? I'm looking for a solution that persists beyond the session, up to 24 hours, for a browser-based app on a receptionist's pc. [Ideally the current session cookie keeps the app alive long enough in that situation; but I want to also make available an alternative approach that will persist under a wider range of circumstances.] – ToolmakerSteve Apr 18 '19 at 11:35
  • last time I looked hard was 2014 so I suggest you try to find a popular and actively used library for authentication for the language / frameworks that you use. – simbo1905 Apr 18 '19 at 11:41
8

I know this question is very old now but I thought it might be a good idea to update the answers with a more current response. For anyone like myself who may stumble across it.

In an effort to increase performance, I was thinking of trying to eliminate a plain 'session cookie', but encrypt all the information in the cookie itself.

Now for the big question: is this a bad idea?

The short answer is: No it's not a bad idea, in fact this is a really good idea and has become an industry standard.

The long answer is: It depends on your implementation. Sessions are great, they are fast, they are simple and they are easily secured. Where as a stateless system works well however, is a bit more involved to deploy and may be outside the scope of smaller projects.

Implementing an authentication system based on Tokens (cookies) is very common now and works exceedingly well for stateless systems/apis. This makes it possible to authenticate for many different applications with a single account. ie. login to {unaffiliated site} with Facebook / Google.

Implementing an oAuth system like this is a BIG subject in and of itself. So I'll leave you with some documentation oAuth2 Docs. I also recommend looking into Json Web Tokens (JWT).

extra

A last note: I'm trying come up with solutions to decrease database load. This is only one of the solutions I'm investigating

Redis would work well for offloading database queries. Redis is an in memory simple storage system. Very fast, ~temporary storage that can help reduce DB hits.

Community
  • 1
  • 1
Cody Wikman
  • 829
  • 10
  • 13
6

Update: This answer pertains to the question that was actually asked, not to an imagined history where this question was really about JWT.

The most important deviations from today's signed tokens are:

  1. The question as originally posed didn't evince any understanding of the need for a secret in token generation. Key management is vital for JWT.
  2. The questioner stated that they could not use HTTPS, and so they lacked confidentiality for the token and binding between the token and the request. In the same way, even full-fledged JWT can't secure a plain HTTP request.
  3. When the question was revised to explain how a secret could be incorporated, the secret chosen required server-side state, and so fell short of the statelessness provided by something like JWT.

Even today, this homebrew approach would be a bad idea. Follow a standard like JWT, where both the scheme and its implementations have been carefully scrutinized and refined.


Yes, this is a bad idea.

For starters, it's not secure. With this scheme, an attacker can generate their own cookie and impersonate any user.

Session identifiers should be chosen from a large (128-bit) space by a cryptographic random number generator.

They should be kept private, so that attackers cannot steal them and impersonate an authenticated user. Any request that performs an action that requires authorization should be tamper-proof. That is, the entire request must have some kind of integrity protection such as an HMAC so that its contents can't be altered. For web applications, these requirements lead inexorably to HTTPS.

What performance concerns do you have? I've never seen a web application where proper security created any sort of hotspot.


If the channel doesn't have privacy and integrity, you open yourself up to man-in-the-middle attacks. For example, without privacy, Alice sends her password to Bob. Eve snoops it and can log in later as Alice. Or, with partial integrity, Alice attaches her signed cookie to a purchase request and sends them to Bob. Eve intercepts the request and modifies the shipping address. Bob validates the MAC on the cookie, but can't detect that the address has been altered.

I don't have any numbers, but it seems to me that the opportunities for man-in-the-middle attacks are constantly growing. I notice restaurants using the wi-fi network they make available to customers for their credit-card processing. People at libraries and in work-places are often susceptible to sniffing if their traffic isn't over HTTPS.

erickson
  • 265,237
  • 58
  • 395
  • 493
  • 14
    Hi Erickson, how can a hacker generate a cookie without the hmac nonce/secret? – Evert Jul 13 '10 at 18:59
  • @Evert I answered that question. – rook Jul 13 '10 at 19:03
  • 1
    @Evert - Sorry, it looks like you edited your post to add the secret while I was posting. With a strong key, the attacker won't be able to forge these tokens, and they can be useful in some contexts. But, it's not clear how you plan to use them. Are you going to keep using HTTPS, but just get rid of an in-memory session that stores the authenticated user identity? – erickson Jul 13 '10 at 19:13
  • 1
    sorry about that.. I forgot to add it and did add it later. HTTPS is unfortunately out of reach for us. I've been pushing it for things like the actual login phase, but people using our system set up their own domainnames, SSL can be a bit complicated and costly. We use PHP and session-data is stored in a MySQL database. We've been experiencing some growth pains and I was trying to find out if I can move the session entirely to the client. It feels nicer to be more 'stateless'. Another way to solve it is to heavily use something like memcache, which I'm also researching a bit =) – Evert Jul 13 '10 at 19:20
2

You should not reinvent the wheel. The session handler that comes with your development platform far is more secure and certainly easier to implement. Cookies should always be very large random numbers that links to server side data. A cookie that contains a user id and time stamp doesn't help harden the session from attack.

This proposed session handler is more vulnerable to attack than using a Cryptographic nonce for each session. An attack scenario is as follows.

It is likely that you are using the same secret for your HMAC calculation for all sessions. Thus this secret could be brute forced by an attacker logging in with his own account. By looking at his session id he can obtain everything except for the secret. Then the attacker could brute force the secret until the hmac value can be reproduced. Using this secret he can rebuild a administrative cookie and change his user_id=1, which will probably grant him administrative access.

Jacob
  • 77,566
  • 24
  • 149
  • 228
rook
  • 66,304
  • 38
  • 162
  • 239
  • 2
    The code was just pseudo code, I intended to add a secret so I did now. I'm well aware of how my platform's session system works it's just that I'm dealing with millions of sessions, and I need to think out of the box. I appreciate the answer, but I'm a seated programmer. Why do you think it's trivial for people to recalculate the secret. Amazon AWS and OAuth heavily use it. – Evert Jul 13 '10 at 18:53
  • 1
    @Evert It is trivial without a secret, with a secret then you require the attacker to brute force this value. A cryptographic nonce is **much more secure**. – rook Jul 13 '10 at 19:00
  • 1
    Not trying to sound defensive, I just try to clarify my situation. If the secret would be based on the user's id and/or password along with a random string, wouldn't this make it considerably harder? Are you saying there is no way this could be done correctly? – Evert Jul 13 '10 at 19:04
  • Bruteforcing is a good point by the way, didn't want to undermine that and it did make me realize it needs to be a very strong key. – Evert Jul 13 '10 at 19:06
  • @Evert Is it possible to obtain all data necessary to build a cookie by looking at the database? If you use the password hash, then this could be obtained with SQL injection, thus defeating the purpose of hashing passwords. – rook Jul 13 '10 at 19:08
  • 3
    It could be argued that when SQL injection is possible, session hijacking is no longer a concern. The prime reason for hashing passwords in a DB, is so a malicious user can't use the passwords for other services the end-user might be subscribed to. So yes, once direct database access is possible a users' session is compromised. I believe this would also be the case for anyone who stores sessions in the database (not that uncommon). – Evert Jul 13 '10 at 19:12
  • @Evert I disagree. If your database is configured properly sql injection should only allow the attacker to select/update/insert into the application's database. In mysql things are a bit different because you can't stack quires, so you should only be able to read data with a sub-select or union select. Thus in this ideal scenario the administrative credentials becomes the largest target for an attacker. The point of hashing the password forces the attacker to break the hash before it can be used. However, if an attacker can build a session id, then you remove this vital security layer. – rook Jul 13 '10 at 19:17
  • @Evert some people remove delete and use a delete column. However, this can be unnecessary, it depends if you care about the data and how many delete operations you need. In MySQL you can't stack a delete query, so it doesn't matter. – rook Jul 13 '10 at 19:25
  • The idea to re-use columns marked as deleted is pretty smart, thank you. The point about reconstructing (admin) session cookies also is valid and well taken. It might be a risk I would take, considering we're actually off worse now. Involving the UA string and ip address might mitigate that a bit (but definitely not solve it). – Evert Jul 13 '10 at 19:32
  • @Evert Well I'm glad your listing to me. So whats wrong with using a purely random number? – rook Jul 13 '10 at 19:36
  • Purely random is definitely good, but the number needs to be stored somewhere.. If it's in the code instead of the DB that seems 'just as bad'. Perhaps (global random number + user's password + sequence number) is a good mix? – Evert Jul 13 '10 at 19:47
  • @Evert PHP's session_start() stores the information in a temp directory, and this is a very common solution. If you hashed that mix of data it could be okay. However the sessions need to time out which is a requirement of CWE-613 (cwe.mitre.org/data/definitions/613.html) Also make sure you read up on OWASP A3: (http://www.owasp.org/index.php/Top_10_2010-A3-Broken_Authentication_and_Session_Management) – rook Jul 13 '10 at 21:06
1

What makes you think this will improve performance vs. secure session IDs and retrieving the userid and time information from the server-side component of the session?

If something must be tamper-proof, don't put it in the toddlers' hands. As in, don't give it to the client at all, even with the tamper-proof locking.

Ignoring the ideological issues, this looks pretty decent. You don't have a nonce. You should add that. Just some random garbage that you store along with the userid and time, to prevent replay or prediction.

Borealid
  • 95,191
  • 9
  • 106
  • 122
  • Currently our sessions are stored in a master-master database, used by an array of webservers. Even though it's a simple BTREE, we want to reduce the footprint. Pruning sessions makes the io go through the roof. – Evert Jul 13 '10 at 18:55
  • 1
    @Evert Are those tables indexed properly? You shouldn't see "through the roof" I/O from that. – doug65536 Oct 05 '15 at 09:37
  • 2
    @DougGale: you're responding to a question of many, many years ago. – Evert Oct 05 '15 at 14:39