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."

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

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: