Don't make cookie-stored sessions a default

Hi!

Before Rails 2.0 is coming, I suggest not to make CookieStore the default session storage. It stores clear-text values on the client-side and the integrity check hash can be brute-force attacked.

I understand that this has been set due to speed advantages, but I believe it's better to make better security a default.

I've written a blog post about this http://www.rorsecurity.info/2007/11/20/rails-20-cookies/

and Corey Benninger presented this at on the OWASP AppSec conference: http://blog.phishme.com/2007/11/owning-rails-20-cookies-at-owasp/

Heiko.

Before Rails 2.0 is coming, I suggest not to make CookieStore the default session storage. It stores clear-text values on the client-side and the integrity check hash can be brute-force attacked.

I posted this on your blog post, but we're probably better off having the conversation here.

There are two distinct problems outlined here. The potential issues when brute forcing your key are exactly why the default secrets aren't chosen from dictionary words. If you have suggestions for ways to improve the randomness of the value chosen, we're all ears.

As for Signing vs Encrypting the data, we'd happily take patches which implemented encryption, and perhaps even made it the default. There's been talk about this in the past but despite the bluster patches never arrived.

It's worth considering the 1.2 behaviour. The default session store is completely unusable when running on multiple application servers and even on a single application server problems with performance and deadlocks are frequently reported.

So almost every prodution rails application has already switched to active_record_store or some other alternative and their apps will continue to use this alternative store. New applications will have the opportunity to make that one line change if they like. If someone wants to prepare a document describing the relative risks and merits of the different session stores, we can make sure it's referenced prominently in the relevant places.

The unworkability of the default pstore sessions forces developers to move to a workable solution using ActiveRecordStore, MemCacheStore, etc. Cookie session store doesn't break, so it doesn't force a move to another system. I wonder if documenting risks will be enough to get people to change when they should.

I had always considered that to be a feature, rather than a defect in design, which reminds developer not store any sensitive/secret data in session.

Your analysis is incorrect; you've based it on the first proof-of-concept code. Please actually checkout the actual Rails source instead of the examining the first changeset on the web. I commented on both your blogs. I'd appreciate if you'd correct your claims.

In my view, the legitimate concerns are:

1) Our explicit choice of transparency with integrity over encryption. See Pratik's comment: this is intentional and aims to correct a perception bug. If you're storing secrets in session, odds are you have a code smell and ought not to. In the rare exceptional case, you can switch to a server-side session store or write a simple EncryptedCookieStore subclass.

2) Session replay attack. This is what Corey should have talked about at OWASP, not the nonexistent brute force attack. A valid session cookie may be intercepted and replayed by an attacker. The cookie store is constructed to make solutions to this simple, such as including a nonce, timestamp, or IP address in the message digest.

Let's put these concerns to bed once and for all:

1) Of those concerned about encoding vs encryption: who's written a plugin to subclass an EncryptedCookieStore which uses the secret to do, say, AES encryption with OpenSSL? I implemented this first, before choosing to go with HMAC, and it's extremely fast and pretty easy to set up. You can use the same session key, even.

2) Of those concerned about session replay: who's explored the possibilities available by passing a block to generate the session key? Let's see plugins implementing per-user keys, nonces, IP binding, and timestamp-based expiry.

I think talking up the solutions to these concerns is far more valuable to the community than spreading fear, uncertainty, and doubt about them. Let's see some code.

Thanks, and best regards, jeremy

The unworkability of the default pstore sessions forces developers to move to a workable solution using ActiveRecordStore, MemCacheStore, etc. Cookie session store doesn't break, so it doesn't force a move to another system. I wonder if documenting risks will be enough to get people to change when they should.

There are a variety of other things which you need to do to remain secure. Put the h in your <%= etc. If we consider it reasonable to rely on documentation for this, it seems it should be reasonable to rely on documentation for the session store.

We can put up a nice page on the rails website outlining the relative merits and risks of each of the session stores, and link to it from the generated environment.rb. Seems like a good enough solution for me, barring any nice refactoring that happens in the meantime.

Thank you for taking up this issue and my apologies if I offended anyone. I realize I linked to an older version of the cookie_store.rb file by mistake, but I believe the risk still remains.

The cookie store option is an unique and interesting solution, however I feel that it creates an inherent security risk (regardless of if the data is encoded, encrypted, or has a HMAC). This is what I was hoping to raise awareness of. I'm sure it will be the perfect solution for some applications, but where I have a concern is that this will be the default store. New developers, especially ones coming from other frameworks, will most likely feel the session is a safe place to store values that should not be manipulated or viewed. Even if the cookie is encrypted instead of encoded, it will leave the session open to the possibility of being cracked and then manipulated.

I think there is a huge advantage to a framework being secure by default. Defaulting to one of the other session store options would be a better stance in this regard. Dealing with CSRF and SQL Injections are security areas where Rails really shines over other frameworks. I'm not sure new developers would necessary read all the documentation about the default session store option until after they've had an issue. I think they would be much more appreciative a more secure solution being the default.

Thanks, ~Corey

Even if the cookie is encrypted instead of encoded, it will leave the session open to the possibility of being cracked and then manipulated.

By this standard isn't ssl, encrypted email, and well any kind of encryption equally fundamentally insecure?

Thank you for taking up this issue and my apologies if I offended anyone. I realize I linked to an older version of the cookie_store.rb file by mistake, but I believe the risk still remains.

Thanks for raising it! The scrutiny is welcome. I do take issue with your conclusions, however :slight_smile:

The cookie store option is an unique and interesting solution, however I feel that it creates an inherent security risk (regardless of if the data is encoded, encrypted, or has a HMAC). This is what I was hoping to raise awareness of. I'm sure it will be the perfect solution for some applications, but where I have a concern is that this will be the default store. New developers, especially ones coming from other frameworks, will most likely feel the session is a safe place to store values that should not be manipulated or viewed. Even if the cookie is encrypted instead of encoded, it will leave the session open to the possibility of being cracked and then manipulated.

Raising awareness of this perception bug is the best answer, in my opinion.

New developers are *better* served by making this the default.

I think there is a huge advantage to a framework being secure by default. Defaulting to one of the other session store options would be a better stance in this regard. Dealing with CSRF and SQL Injections are security areas where Rails really shines over other frameworks. I'm not sure new developers would necessary read all the documentation about the default session store option until after they've had an issue. I think they would be much more appreciative a more secure solution being the default.

Let's not confuse secrecy with security. Session cookies typically store a user_id and a flash message. We only need to ensure that the user_id is not changed, and hmac-sha1 provides that beautifully. This is not by any stretch insecure.

Best, jeremy

Thanks for all the answers and my apologies for the wrong link on my blog, as well. I did, however, check it with the most recent version, and I still believe that the risk remains, brute-forcing is a possible threat. Whether or not it will be successful is another question. That depends on the strength of the secret and I do like the solution of a generated secret for new applications. But I believe educating (even though it's a small threat) is better than keeping it a secret, even for new developers, so I raised that issue, that there will be no weak secrets from the first. Maybe I'll update the wording of my article.

So far there are not many real-world examples, but here are the only secret keys I found on Google: to2, ias, random seed, this_can_be_long, some secret phrase, getyourownsecretkey, with_my_last_breath_i_curse_zoidberg

Some are examples, but chances are cargo-culting will be done. Especially the first 3 are very dangerous, and there were only 2-3 hashed secrets, the rest are made up of words from dictionaries. How about an Exception when the secret is to short or not a hash, that would eliminate the problem?

I know that most session normally store a user_id, but there are a lot of other sessions (a Google code search shows a lot of examples) and chances are among these are information the user is not allowed to see.

Another problem raised is session replay: If someone stores state (is_admin, points,money, whatever) in a session, it can be replayed. This doesn't happen for server-side cookies. With IP-, timestamp- or per-user-based solutions the same user can still do the same request again (maybe in a short timeframe). Hmm, a nonce could do it maybe.

Ok, these are 3, maybe 2.3:) problems with client-side store over server-side store. If most will switch to another session store, why make it a default? I know CookieStore works well for a lot of applications, but w/o fully understanding it, I believe a server-side solution is more secure.

I'm willing to write up an educational document about the different options, who's going to eliminate the threat of session replay?

Just to be absolutely clear, you can feel comfortable using session cookies if you only store a user_id and a flash message in the session, right? (Sorry, I'm just getting a bit confused.)

> Let's not confuse secrecy with security. Session cookies typically > store a user_id and a flash message. We only need to ensure that the > user_id is not changed, and hmac-sha1 provides that beautifully. This > is not by any stretch insecure.

Just to be absolutely clear, you can feel comfortable using session cookies if you only store a user_id and a flash message in the session, right? (Sorry, I'm just getting a bit confused.)

Yes.

Ok, these are 3, maybe 2.3:) problems with client-side store over server-side store. If most will switch to another session store, why make it a default? I know CookieStore works well for a lot of applications, but w/o fully understanding it, I believe a server-side solution is more secure.

I don't know why most people would shift. Folks who follow best practices can reap all the benefits and are not exposed to any of the drawbacks. The cookie store shines a spot on poor development practices, like keeping is_admin in a session. And we're not in the business of under-designing the framework in order to protect people from themselves. The same charge holds true against HTML escaping and SQL injection. If you don't use the facilities that Rails gives you, like h() and [ "user_id = ?", user_id ], then it's possible to create insecure applications.

The CookieStore offers substantial improvements in usability, such as zero maintenance, works without a database, and speed. Giving up all of that because someone, somewhere might put secrets in the session seems like a bad trade.

I'm willing to write up an educational document about the different options, who's going to eliminate the threat of session replay?

That's excellent. It would be great to inform people about the trade- offs involved with the various session stores. Especially if done with an eye on best practices (don't keep secrets in the session). And I too would be happy to see extensions to the session store that could give people both encryption and replay protection. Most applications won't need either, but it would extend the cases where the CookieStore could be used from most to pretty much all, which would be a win.

I think this thread is mostly about picking the right tone for the debate. I don't think we improve the discussion by going for all-out alarmist. We're all working to make Rails better. We may disagree on the value trade-offs, but that's how it should be.

CookieStore sounds great to me, as it allows client data to be on the client. That's basically what I'm doing now, with sessions turned off in Rails 1.2.5... storing a user's id up in a Cookie and doing database queries for the rest.

My concern is synchronization issues when relying on server-side session data too much. It's quite easy to drop a variable in session to use in the next request. A user hits the browser back button and things are left in an inconsistent state.

Sessions already use a cookie, so for many applications it makes sense to pop the essentials (user id) into a cookie and otherwise architect without sessions. Flash remains useful for non-Ajax requests, so using the CookieStore for that could be a plus.

In prior large non-Rails applications, we dealt with hundreds of thousands of session records in the database per day. It was too slow to delete a subset of records, so we just truncated at night. Less than ideal, the cookie solution is simple.

Best practices documentation would be a great thing for the community!

More on RESTful state transaction models:

"To achieve a RESTful state transaction model, in a nutshell, means passing enough data back and forth so that a user can click on any feature or function of the application, in any order, and the server will (1) know what to do every time without fail, and (2) will not 'remember' each client's previous states. This implies that the server is 'stateless' with respect to specific client data. The server has a state of it's own, and knows it's own transaction state, of course. But it is only 'aware' of each client's state when it is contacted by the individual clients, and the client notifies the server of it's state."

http://www.devchix.com/2007/08/16/restful-thoughts-on-a-web-20-python-project/

Some are examples, but chances are cargo-culting will be done. Especially the first 3 are very dangerous, and there were only 2-3 hashed secrets, the rest are made up of words from dictionaries. How about an Exception when the secret is to short or not a hash, that would eliminate the problem?

I'm not sure how we could decide if something was or wasn't a hash. But I've added code that raises if the secret is fewer than 30 characters. Seems like a decent stand-in

I know that most session normally store a user_id, but there are a lot of other sessions (a Google code search shows a lot of examples) and chances are among these are information the user is not allowed to see.

This is potentially an issue, but using encryption instead of hashing would take care of that, right?

Another problem raised is session replay: If someone stores state (is_admin, points,money, whatever) in a session, it can be replayed. This doesn't happen for server-side cookies. With IP-, timestamp- or per-user-based solutions the same user can still do the same request again (maybe in a short timeframe). Hmm, a nonce could do it maybe.

We had a long discussion about replay attacks and a nonce requires some form of shared storage which removes the reason for a cookie store. What we could provide is some kind of 'session sanity check' hook that users can override with their own code. Checking whatever they want, timestamps, IPs etc.

The threat of a third party replaying a session doesn't worry me too much. If they have access to the value in that session cookie, then they also have access to the key used for a server side session store which is just as dangerous.

The case we need to be careful about is people storing credits etc in their session and continually restoring the old session once they spend them all.

My concern is synchronization issues when relying on server-side session data too much. It's quite easy to drop a variable in session to use in the next request. A user hits the browser back button and things are left in an inconsistent state.

That's session-store independent, though. You'll get precisely the same behaviour if you store the inappropriate data in a cookie.

Sessions already use a cookie, so for many applications it makes sense to pop the essentials (user id) into a cookie and otherwise architect without sessions. Flash remains useful for non-Ajax requests, so using the CookieStore for that could be a plus.

Using the cookie store for the flash certainly seems like a win to me -- if the user plays silly buggers with the values in there, at worst they're only going to hurt themselves. No need to waste disk space or database CPU storing and retrieving that stuff all the time.

On the other hand, though, storing things like user ID in the cookie store is pointless. All you're doing then is using the ActiveRecordStore but with a different key to retrieve stuff (user ID instead of session ID). If you store multiple IDs in there (user ID and cart ID, for instance) then your performance will likely be worse, because you're hitting the database multiple times to retrieve both of them, rather than just the one chunk of session data.

You can't possibly get away with *not* loading the models identified by the IDs in the cookie, even if you only need the ID, because you can't possibly trust the information that the user is providing -- mangling the user ID to log in as someone else (if the hash is easily breakable), or even just your basic common-or-garden-variety replay attack (nonces have the same server-side storage problem as the data itself, and timestamps give an easily readable window, while encryption just leaves the attacker to guess how long the window is, it doesn't close the window). So you need to validate the ID(s) the user is providing before relying on it/them.

At least with a server-side session store (of whatever variety) session keys are large enough that guessing them isn't feasible (whereas most everyone still seems to use sequential integers for user IDs, rather than UUIDs), and you as the system operator can invalidate a session key just by clearing the associated data. Once you give data to the client, you lose that much control of it.

I suppose you might get some performance benefits if you store IDs in the cookie that are only needed on very few pages, so you don't have to validate it very often. I'm not guessing that there are many webapps out there that could benefit from that particular brand of optimisation (although the flash storage could probably benefit a lot of people -- anyone who runs a purely- or partially-public Rails site that uses the flash on their public pages).

Anyone want to start a pool on how long it'll be before someone re-implements ActiveRecordStore on top of CookieStore by just storing a session_id in the cookie and then loading up an arbitrary chunk of data in the database based on that value? I'll take 3 months.

Actually, that gives me a bit of an idea -- you could prevent session ID guessing on a server-side store by making the session ID key:hash, where key is what we have now and hash is {md5,sha1}(key+secret). Then the server can validate the session ID with no more than the session ID and the secret. Doesn't stop replays if you don't expire your sessions, but at least slows down people guessing.

At any rate, I'm not anti-CookieStore, because I just don't care. I've got my template Rails project with all my customisations and plugins, and if I have to change another config option or two, that won't be a big deal for me. Defaults are always going to piss someone off, so I've given up worrying about them. At any rate, a lot of the tradeoffs in client-side stores are similar to server-side stores, just in different clothes, so there isn't such a big win. I suspect that I might use an encrypted CookieStore on apps that don't have a database behind them, just to save me a cron job.

If someone's in a wish-granting mood, being able to store the flash in a cookie (either a separate one, or as part of the session ID one, isn't a big deal) while storing the session proper in whatever store you've configured would be wonderful, and that session ID guessing thing isn't patented or anything (at least not by me <grin>).

More on RESTful state transaction models:

"To achieve a RESTful state transaction model, in a nutshell, means passing enough data back and forth so that a user can click on any feature or function of the application, in any order, and the server will (1) know what to do every time without fail, and (2) will not 'remember' each client's previous states. This implies that the server is 'stateless' with respect to specific client data. The server has a state of it's own, and knows it's own transaction state, of course. But it is only 'aware' of each client's state when it is contacted by the individual clients, and the client notifies the server of it's state."

devchix.com at Directnic

I can't manage to read that snippet in any way that isn't either horribly insecure ("trust what the client is giving you") or is precisely how things are done now ("client gives me some identifier, I retrieve the current state of the session from my 'server side state'"). Perhaps the rest (heh) of the article provides a bit more detail, but reading articles written by RESTful advocates irritates me for some reason. I like REST, but RESTers bug me. <grin>

- Matt

On the other hand, though, storing things like user ID in the cookie store is pointless. All you're doing then is using the ActiveRecordStore but with a different key to retrieve stuff (user ID instead of session ID). If you store multiple IDs in there (user ID and cart ID, for instance) then your performance will likely be worse, because you're hitting the database multiple times to retrieve both of them, rather than just the one chunk of session data.

The normal usage is: you store the user_id in the cookie and perhaps a flash. Nothing else. When a request comes you directly load the user. You trust the ID because there's a mechanism that validates data integrity.

With the database store you have to hit the database _twice_. One to load the session given its key, and another one to load the actual User. Of course you don't store instances in the session, that's a bad practice. So in terms of database hits cookie storage is much lighter.

In terms of maintenance a win, something less to care about in the server.

You can't possibly get away with *not* loading the models identified by the IDs in the cookie, even if you only need the ID, because you can't possibly trust the information that the user is providing -- mangling the user ID to log in as someone else (if the hash is easily breakable),

But it isn't with default settings! That's the whole point of providing this feature.

-- fxn

I always find with security people that they are good at talking about theoretical risks, but poor at providing evidence that the actual risk is significant.

Can somebody show a security violation with correctly architectured Rails application (ID/Flash session)?

Without evidence it's just a conspiracy theory.

Hey,

I like your key+hash idea Matthew. I assume that would be faster than a decryption scheme, but still more secure than storing 'id:uuid', which I do now. I'm not quite so crazy as to store a straight numerical id, but not as up on the security concerns as I should be.

From that uuid cookie I load my appropriate ActiveRecord to reauthenticate. So every request involves a quick auth, and roles wouldn't be stored session side (causing problems if they are changed by an admin). I'm more interested in caching the real tables than using session data, especially if session data is in the database only.

While the architecture and the session-store are independent, I do like the simpler Cookie Store approach for my particular case. In a situation where the user login is up front, this works well... in other situations session data may be harder to do without.

It is actually a bit different from using ActiveRecordStore. These tokens are "permanent" user account records, not session data that needs purging. The cookie expires on its own. Of course the last login time can force a visitor to login again.

The cookie is just for the absolute essential, and I would probably consider HTTP Basic (behind SSL) or Digest Auth as an alternative. That means anything not permanently in the database is passed through the [nested] routes (which doesn't suffer from back button issues).

This app I'm working on is a tool to build forms, somewhat like Wufoo. At one time, clicking a form element to add would check the session data for which page it's adding to. I changed it to nest the element under pages and go by the url line. If a user goes back to another page without the server knowing, the session data would be out of sync. Re-architecting the routes a bit was more work, but elements always get added to the right page.

Having client data on the client is a make sense proposition, if not for security concerns... which are certainly valid. I think I need to look into that side more to find a happy medium.

- nathan.

I always find with security people that they are good at talking about theoretical risks, but poor at providing evidence that the actual risk is significant.

Can somebody show a security violation with correctly architectured Rails application (ID/Flash session)?

If you're *just* storing an ID and the flash, then the only danger is the rather infeasible one of someone cracking your secret by brute forcing the hmac.

The additional risk is that because cookie sessions can't be expired, an attacker with access to your cookie will be able to replay that login indefinitely. Unlike the server side stores where someone gaining access to your cookie only has access for as long as it takes for you to clear out the server side store.

Without evidence it's just a conspiracy theory.

There are real risks with the cookie store, and it's good to be discussing them openly. Just because they're not OMFG THE WORLD IS OVER doesn't mean they're not risks :wink: