Replay attacks with cookie session

I'm concerned about the possibility of replay attacks with cookie
sessions. This is a standard security issue.

Example:
1. User receives credits, stored in his session
2. User buys something
3. User gets his new, lower credits stored in his session
4. Evil hacker takes his saved cookie from step #1 and pastes it back
in his browser's cookie jar. Now he's gotten his credits back.

This is normally solved using something called nonce - each signing
includes a once only code, and the signer keeps track of all of the
codes, and rejects any message with the code repeated. But that's
very hard to do here, since there may be several app servers
(mongrels).

Of course, we could store the nonce in the DB - but that defeats the
entire purpose!

You and I think alike. I was just considering this the other day as well. The trivial fix is to say "don't store sensitive transient data in the session". I think that leaves something to be desired, but it is probably the preferred route unless there is a clean technical solution.

I'm concerned about the possibility of replay attacks with cookie
sessions. This is a standard security issue.

Example:
1. User receives credits, stored in his session
2. User buys something
3. User gets his new, lower credits stored in his session
4. Evil hacker takes his saved cookie from step #1 and pastes it back
in his browser's cookie jar. Now he's gotten his credits back.

Interesting, I hadn't considered that scenario.

This is normally solved using something called nonce - each signing
includes a once only code, and the signer keeps track of all of the
codes, and rejects any message with the code repeated. But that's
very hard to do here, since there may be several app servers
(mongrels).

This sounds like something you could perform at the application level.

jeremy

> I'm concerned about the possibility of replay attacks with cookie
> sessions. This is a standard security issue.

Interesting, I hadn't considered that scenario.

IMO, we need to rethink moving to cookie sessions by default, then -
if a core member hadn't thought about, what can we expect from
beginners? Last thing we need is a reputation for being weak in
security.

Before everyone responds: "It will help performance, and good
developers don't store volatile data in the session, anyway!" I'll say
that we've seen lots of companies (who won't be named) sacrifice
security in order to gain out of the box performance or ease of use -
and it comes back to haunt them. Secure by default, and let the
advanced users make it less secure to improve performance - not the
other way around.

> This is normally solved using something called nonce - each signing
> includes a once only code, and the signer keeps track of all of the
> codes, and rejects any message with the code repeated.

This sounds like something you could perform at the application level.

I don't think so - as long as step 2 (in my example) goes to one
Mongrel, and step 4 goes to another, the attack succeeds. I can try
repeatedly with a script until this happens. The only way to protect
is to use a shared backend like the DB or DRB - but then you've lost
the point of cookie sessions.

IMO, we need to rethink moving to cookie sessions by default, then -
if a core member hadn't thought about, what can we expect from
beginners? Last thing we need is a reputation for being weak in
security.

I think that's a bit sensational. The cookie store has gone through a
couple of evolutionary steps already and is ready for more, if need
be.

Before everyone responds: "It will help performance, and good
developers don't store volatile data in the session, anyway!" I'll say
that we've seen lots of companies (who won't be named) sacrifice
security in order to gain out of the box performance or ease of use -
and it comes back to haunt them. Secure by default, and let the
advanced users make it less secure to improve performance - not the
other way around.

Let's see a brainstorm of solutions and other potential vulnerabilities.

> > This is normally solved using something called nonce - each signing
> > includes a once only code, and the signer keeps track of all of the
> > codes, and rejects any message with the code repeated.

> This sounds like something you could perform at the application level.
I don't think so - as long as step 2 (in my example) goes to one
Mongrel, and step 4 goes to another, the attack succeeds. I can try
repeatedly with a script until this happens. The only way to protect
is to use a shared backend like the DB or DRB - but then you've lost
the point of cookie sessions.

To some degree. I'm sure there's more to this, though. I'm not a crypto expert.

jeremy

I'm neither a Rails nor cryptography expert, but the threat of
session hijacking is a very important issue, and due to compliance
requirements such as SOX, sites must think even more seriously about
what is being stored in sessions and cookies, and take reasonable
measures to protect them.. preferably by default.

I'm saddened that many recent Rails books don't seem to cover this
issue. If the problem had more clarity and direction I think that'd
go a long way to easing enterprises into the Rails community.

Preston

I'm not a crypto person, but what if a version number were embedded
with every write to the cookie session? Of course, check the data
+version against a hash to make sure they didn't tinker with the
version.

That’s essentially what a nonce is, but:

  1. Nonce is random and can’t easily be guessed

  2. It still doesn’t solve the multiserver problem: how does one server know what the current version number is when there are 10 Mongrels?

Heh… how about a user ID? What if an evil hacker sets it to 1, assuming that the first user is likely to be admin?

Alex

He can’t, the HMAC wouldn’t verify.

Ah, that’s right. So the only possible attack vector here is copying the entire cookie, including HMAC?

OK, how about making this option available, but not default? And documenting explicitly that if you switch to this option, you should be extremely cautious about putting what you called “sensitive transient data” in the session? This looks like a good compromise, because people with a clue would still use it (for both maintainability and performance benefits) while people without a clue will have to figure it out, and hopefully read the warning in the docs.

Personally, I’d much rather deal with beginners having “Rails performance sucks” pains early on (because of file-based sessions), rather than discovering that “Rails security is totally screwed” a year after production.

Alex

It's an important issue when you're trying to have a secure session. When you're just using the session for some flash variables I don't see the point in using an overly secure session system.

I think we should document the various session stores very well and tell people what to use in which circumstances.

Developers aren't small children, we don't have to make their decisions for them.

Manfred

I'm concerned about the possibility of replay attacks with cookie
sessions. This is a standard security issue.

Example:
1. User receives credits, stored in his session
2. User buys something
3. User gets his new, lower credits stored in his session
4. Evil hacker takes his saved cookie from step #1 and pastes it back
in his browser's cookie jar. Now he's gotten his credits back.

This is normally solved using something called nonce - each signing
includes a once only code, and the signer keeps track of all of the
codes, and rejects any message with the code repeated. But that's
very hard to do here, since there may be several app servers
(mongrels).

Of course, we could store the nonce in the DB - but that defeats the
entire purpose!

>

Your example scenario is unrealistic. If you really think it's a good idea to store such important stuff as your user's credit in a session instead of in your database you probably have bigger issues to worry about. :slight_smile:

As long as you only use the session to maintain the id of the authenticated user and for flash messages there's absolutely nothing to worry about.

Kind regards,
Thijs

P.S. If you really _do_ think it's a good idea to store your user's credit in the session, please google for 'share-nothing' and 'database transactions' and do some reading.

Sure, but…

Not everybody who develops web apps knows about “do not keep anything interesting in sessions” mantra, or agrees with it. At least a couple of times I had to explain it to people who were not clueless by any standard.

It’s actually a rather arcane bit of knowledge, and if you don’t have it, you may discover it the hard way, when it’s too late.

Which makes session storage in cookies a good option, but perhaps not a good default (?)

Alex

For those just tuning in… :slight_smile:

This is the crux of the issue… of course it’s a terrible idea to store sensitive or transient data in the session, but the question is one of API design. Do we want the penalty for ignoring best practices to be compromised security?

I happen to think it’s not a huge deal if we document properly. Web developers need to understand all of the abstractions that Rails builds on top of HTTP in order to build a secure web app. It’s the same issue as people who throw things like

into their online stores. If you don’t understand how the Web works, you won’t build secure applications, no matter what framework you work within. When PStore was the default, professional developers understood what that meant and what freedoms and limitations it afforded us. Now that CookieStore is the default session store, we are responsible for understanding what that means.

Don’t get me wrong; the Rails team has a part to play in this – documentation. To the extent that we give the impression that the session is a “big hash in the sky”, people will put stupid things in it.

After writing this and thinking about it, I realize that authentication may be a problem. It makes me slightly uncomfortable to hand the client a token saying, in effect, “I am logged in as Joe User” with no qualifications, signed by the server. And that token never expires on the server side – you could come back 5 years later and prove your identity. There is no way to selectively expire sessions, e.g. based on time. You can change the secret, but that expires all sessions including current ones. You can send the client a new cookie that invalidates the old one – but he can always ignore it. He still has the valid authentication cookie from earlier, and it will still work.

I would feel a lot better about it if it incorporated a nonce or some other form of time-variant information. I can’t come up with an attack other than the replay attack, but that “I am logged in as Joe User” message seems too general to make me feel completely comfortable about authentication via cookie sessions for the time being.

Brad

It's even more complicated. Defining "sensitive or transient data" is
not at all trivial.

The standard use case for a cookie session is store only flash and
user id. Not sensitive or transient? Okay.

Now, I click "Log Out", and get up from the library's computer, only
to let the person waiting after me to retrieve the old cookie.... That
innocuous user id just became both sensitive and transient.

The point is, answering these questions is hard. Witness the
confusion in this thread alone. DIY cryptosystems are hard:
professionals fail. WEP failed. Does it make sense to push all these
questions onto each new Rails developer's shoulders?

And I think that is exactly what it comes to. That's kind of where I landed with that post when I started considering what user_id really meant.

As neat as cookie sessions are, I don't think I'm going to use them to store authentication info anymore. Too many question marks.

This problem can be addressed in a lot of ways. One of the easiest approaches would be to use a filter to process the validity of a cookie. Have an after_filter that adds a “single-use” token to the cookie after a request and a before_filter that checks for the “single-use” token when a user makes a new request. That way a replay attack wouldn’t work.

Bundling in something like this into Rails makes a lot of sense. It would be opt in by adding the filters into application.rb and not forced on anyone by default.

This was discussed earlier in the thread. The problem is that such a nonce would require communication between the backend servers via DRb or the database, which removes any benefit from storing the cookies client-side. You might as well store the whole session in the database or DRb store.

As for the opt-in, if you’re the type of user that would know enough about security to opt in to such a plan, you’re probably not storing account balances in a client-side cookie. The discussion we are having concerns sensible defaults.