Pothibo

How bad is rails' cookie system?

Rails cookies and session handling made the headline last week for all the bad reasons. Some raised concerns that rails was poorly handling sessions. It was, consequently, vulnerable to replay attack. If you have no idea what I'm talking about, go read this article first.

When the news broke off on hacker news, I already had concern about how rails uses cookies to handle sessions. However, I never really made up my mind as whether or not the current implementation was a bug/exploit. So when I saw the post on HN, I didn't comment about it since I didn't have anything to add to the conversation, unfortunately.

Now, I think I've made up my mind and I'm ready to talk about it.

Before I make my point, I wrote a post about the session system on rails a few weeks back. You should read it if you haven't yet as it gives a nice overview of what options are available under Ruby on rails 4.0.

If someone/something sniff your cookie, you are DONE

HTTP is a stateless protocol. That means no state can be preserved between two requests. In order to add states on web server, cookies were created. This store values on the browser that are set by the server. On every request, cookies are sent to the server so it's possible to identify who's making the request. Nothing new here.

So, if someone steal your cookies, they steal your identity. Remember Firesheep? It is a small Firefox extension that was sniffing the local network to get cookies. You could sit in a Starbucks, connect to the unencrypted WiFi hotspot provided by Starbucks, launch Firesheep and have access to everyone's facebook & Gmail that were connected to the same Wifi as you.

This is why, when Firesheep made the news, everyone (facebook, gmail, twitter, etc.) rushed to use https(SSL). SSL isn't perfect but it does add a layer of security since it encrypts the transmission of data. So even if someone sniffs your network activity, they will have to decrypt the data before having access to your cookies.

Leaking your cookies equals to giving people a temporary password to your accounts. To most of you, this doesn't come as news but I wanted to take the time to explain it anyway. I think this kind of information needs to be repeated because it's paramount to your security.

The fact that your cookies alone are signed, encrypted and whatnot doesn't change a thing in the previous scenario. The purpose of an encrypted, signed cookie is to make sure someone can't forge a cookie to impersonate someone else.

This isn't specific to rails. This is the expected behaviour for pretty much every framework there is.

The CookieStore is far from perfect

It's not because cookies are insecure by default that rails can get away with its own issue. The cookie sent to the browser is actually a marshalled Hash with a session ID and the user ID. With the current state of ActionDispatch::Session::CookieStore, it's impossible to disable a cookie on the server side. That means if someone has the encrypted cookie for user ABC123, that someone could log in as ABC123 forever. There's two reason why this is happening.

  1. No timestamp is set in that hash so it's impossible for the server to know if the cookie is expired and,
  2. The session ID means nothing.

When I say the session ID means nothing, I mean absolutely nothing. Everyone could have the same session ID and it wouldn't change a thing from rails' perspective. No conflict, nothing.

And this could be where the answer is. If the session ID would be stored somehow on the server, it would then be possible to disable a session and every cookies linked to that session would be invalidated by association.

Of the available choices for storing session, CacheStorage solves that issue.

By default, ActionDispatch::Session::CacheStore uses Rails.cache which is an instance of ActiveSupport::Cache::FileStore. It also uses ActiveSupport::Cache::Strategy::LocalCache as an in-memory buffer that will be used when your cache is hot so you don't read from the filesystem every time you read a value from the cache.

Turn session ID into something useful

So, in order to fix the logged in forever issue, the solution is to make the session ID something that needs to be valid. The cache store does exactly that. How do you enable it? Well, you have to set it up in the initializer.

#config/initializers/session_store.rb

YourApp::Application.config.session_store :cache_store, key: "your_application_key"

With this very small change, rails will now send a unique ID and everything else will be stored on the server side. If someone logs out of their account, the session information is destroyed on the server side and anybody who would try to log with the cookie won't be allowed in.

Because it's a cache on the server, there's a few things to consider

  • The cache has a limit of sessions it can hold in memory, everything else is stored on disk,
  • Fetching data from disk (cold cache) is very long. It will impact performance,
  • Cookies are now sent unsigned and unencrypted. Security has moved from encryption, to server side storage.

Now, I hope you understand a little bit more how the session is handle under rails. If you have a question, hit the comment box below!

Get more ideas like this to your inbox

You will never receive spam, ever.