Session of Fear
Posted by Christopher Wojno Fri, 10 Aug 2007 23:48:00 GMT
Now, before you think “FUD Time!”, I preface this post with the following warning: Yes, this is a problem with not just Rails but all sessions; no, it’s not unsolvable. So, in light of the bad news, there is much good news.
The Problem
Rails applications are very useful. However, most require sessions in order to be useful. Sure, you can provide an ActionWebService or REST-based service to not use sessions, but for the majority of user services, you’re stuck with sessions.
A Session About Sessions
(Go ahead and skip this part if you’re already familiar with how sessions work)
A session is a way a server remembers who you are (in case you didn’t know). The most common way of allowing a server to ID you, without knowing who you are exactly, is by sharing a secret! Well, it’s supposed to be secret and we’ll come to that in a bit. When a new session is created, your session ID is generated (it’s random, hopefully) and saved in the server. The id can be associated with other data, such as your name, or permissions. When you request a page, the server will give you this ID. You almost never see that ID because it’s sent in the background as a “cookie.” You’ve probably heard of these before. From now on, every time you request a page from the server, your cookie data is automatically included in the request (your web browser takes care of this) so the server can figure out who you are. Done correctly, no personally identifiable information is sent over the Web. Just your (hopefully) random ID.
Sounds good so far. But if you’re not using HTTPS (SSL/TSL encryption), your secret contained in that cookie can be seen when it is first sent, and when you access any page thereafter (because you’re sending it each time remember). So, if someone has access to your traffic (if you’re using a shared WiFi or a hub or have a shady ISP), they can pull out your secret. Here’s an old session I sniffed using a program called snort:
GET /javascripts/prototype.js?1186461045 HTTP/1.1.. Host: christopher.wojno.com.. User-Agent: Mozilla/5.0 (X11; U; FreeBSD i386; en-US; rv:1.8.1.6) Gecko/20070810 Firefox/2.0.0.6.. Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5..Accept-Language: en-us,en;q=0.5.. Accept-Encoding: gzip,deflate.. Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7.. Keep-Alive: 300.. Connection: keep-alive.. Cookie: _session_id=db392fa5b39125fa7c1e581b9c1ec71d; is_admin=yes....
There it is, the last line. “Cookie.” So when my browser was trying to download some javascript libraries (Prototype is quite good), it sent my session ID. Now, the javascript doesn’t care about my session ID, but it was sent any way. If someone were to see that sent over the Internet (at any point on it’s way to the server), he or she could create a fake cookie with that ID and login as me without my password! (don’t try it, I’ve already logged out and reset the session) They could then, go into the settings and lock me out of my own system (until I change the password in the ruby console… that’ll show ‘em?). In the meantime, the damage has been done and it can happen again.
SSL!
I won’t go into SSL, but that little piece of technology has enabled credit card purchases over the Internet for years and will hopefully continue to do so for years to come. Any way, in short, it’s encryption and it will encrypt cookies too!
So, we need to force rails to encrypt cookies. Not as simple as it sounds. First, you need to setup an SSL certificate on your server. Easier said than done, though, some Apache packages come with the tools to automatically generate one. For a confusing, yet interesting read, see X509 on Wikipedia.
I assume you installed your SSL certificate and have configured Apache1 to run with SSL (or lighty, if you’re using that instead (mongrel is not mentioned as it does not have SSL, though there is a way to use SSL and mongrel together)). If not, there are loads of tutorials. I warn you though, it’s fairly technical.
Hold onto your Cookies!
So, you have SSL and a Rails application running (I’m assuming). How do you tell Rails to make sure your cookies are only sent via SSL? Rails lets you specify it.
Tell Rails to use SSL Only
By default, Rails does NOT enforce SSL on cookies or sessions (that would be frustrating for development, wouldn’t it?). So you have to enable that enforcement yourself. If you want your session cookie to be sent over SSL site-wide (and generally, you do!), head into your rails application directory and open (in your favorite editor) app/controllers/application.rb Add the following anywhere in the ApplicationController class:
class ApplicationController < ActionController::Base session :session_key => '_session_id', :session_secure => true end
The ApplicationController class definition should already exist, don’t duplicate it. Also, make sure that session isn’t already specified. If it is, the important part here is ”:session_secure => true”. Rails will now tell the browser to only send the session cookie if the browser is using the https (SSL) protocol. This feature is poorly documented but hopefully this will help keep your applications that you write, safe. NOTE: You MUST use SSL if you enable this or your application will become extremely forgetful (Who are you? What are you doing in my kitchen?!).
If you’re interested in storing OTHER data (not overly recommended though due to this and another exploit) in other cookies, Rails offers cookie—manipulation (not really management, the API leaves something to be desired). You can tell individual cookies to only be sent over encrypted connections, just like the session cookie. The other exploit: if the computer is shared and the browser doesn’t clear out the cookies, the next person to sit at the computer can harvest the cookie information, so don’t store passwords, e-mail addresses… really anything in cookies, it’s just a bad idea. DO store worthless information you don’t want to save in your server, such as squirrel preferences.
Admittedly, generally the odds of someone having access to your network traffic (and your cookies, no! MY cookies!) directly is moderate. For the attacker to successfully pick out your cookie data from the slew of traffic wizzing by, he/she would have to be looking and looking for a specific cookie name. So make sure no one has it out for you.
I don’t mean to sound down on the Rails team. Dang-fine-job I say. Cookies aren’t really important and the session is cookie-based, so session security falls by the way-side. It’s up to all developers to keep his or her eyes open for potential pitfalls.
1_000.thank( Rails::DevTeam.members.collect{|m|m.email} )
1 Note: Apache doesn’t know how to run Ruby code as of this writing. You need to use an Apache’s mod_proxy. This will (after it’s been configured) then pass the requests from Apache, to mongrel, lighty, or… WEBrick (if you’re nuts)
