Archive

Archive for the ‘Web Development’ Category

Rails error: Session contains objects whose class definition isn’t available

January 17th, 2012 No comments

I am writing a rails3 application where I am storing some custom objects in the session. All goes well until I restart my server and all the existing sessions start throwing this error:

Session contains objects whose class definition isn't available.
Remember to require the classes for all objects kept in the session.
(Original exception: uninitialized constant Map [NameError])

Specifically I have a geographical map class (Map) and I am storing the map object in sessions. Rails cannot unmarshal the serialized session representation into the map object until it knows what the Map class looks like.

To overcome this problem, I added the following line of code in my config/locales/application.rb file and it solved the error for me:

 require “#{Rails.root}/app/models/map”

where my map class is defined in /app/models/map.rb file.

Though it is discouraged to store complex objects in sessions, I got to know about this taboo much later in my development cycle. At that point, this fix was easier for me than to rewrite major pieces of code.
I hope this helps someone on the other side of planet who is banging his head over this peculiar error.

 

Categories: Code, Ruby on Rails, Web Development Tags:

Rails applications using cookie based session store for session management are vulnerable to Replay Attacks

July 18th, 2011 No comments

Rails3 uses Cookie Store as the default for session management . This is a significant (and interesting) change that has a couple implications on the security of web application that rely on cookies for session storage. People have already illustrated how replay attacks can subvert your web applications.

This article demonstrates a replay attack where – in certain cases – your login based application’s logic can be subverted to bypass the authentication and directly impersonate an user (whose cookie you’ve managed to sniff/steal).

This post will:

  • Illustrate how sessions are stored in cookies instead of server side storage.
  • Demonstrate a common mechanism in web applications to identify if the user is authenticated.
  • Show how can a stolen cookie can be replayed to impersonate a valid user (even after the real user logs off).

In cookie based session stores, all the session data is stored in a cookie as a hash map of key:value pairs. Everything that you set in a session is serialized into a stream of bytes – base64 encoded – and set as a browser cookie. Hence, all of your session variables actually end up being stored on client side. From then on, your browser sends all your session data to the server in each single request you make to the website.

However there is a security mechanism that prevents from any user from tampering the session data stored as cookie – and that is the HMAC of the session byte stream calculated from a server side secret. So here is how it works:

  • When you do a session[:valid] = “true” in a rails application, the application converts it into a hash, serializes it into a byte stream and calculates a HMAC of this stream.
  • This HMAC is appended along with the session object in the cookie with a ‘- -’ delimeter.
  • The browser sends this cookie (session object–HMAC) with every request to server.
  • For every request, the server calculates the HMAC of the session hash that browser sent in the cookie and compares it with the accompanying HMAC in the cookie. If they don’t match, the session hash (or the HMAC) has been modified on client side – the session data will be ignored in this case.

Now lets see a common way how sessions are managed in many web applications.
Suppose your web applications’ authentication routine looks like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def verify
if params[:username]=='demo' && params[:password]=='demo'
flash[:message] = "Login Successful! Welcome to the member area"
session[:valid] = "true"
session[:name] = "Shivam Patel"
session[:email] = "shivam@cmu.edu"
session[:password] = "commack@782"
session[:credits] = "780"
redirect_to '/members/profile'
else
flash[:message] = "Your username/password is incorrect. Did you try demo/demo :)"
redirect_to '/auth/login'
end
end

The code above checks for the valid credentials, and sets some session variables once the user is authenticated. A common technique employed for session management is to set a special session variable once an user is authenticated. Thereafter for each request by the browser, a simple check is performed to see if that session variable is set or not. If its set, the requested ‘member only’ page is displayed else an error is displayed asking user to login to access that page. This value is cleared when user presses logout. In our code above, session[:valid] serves the purpose of the ‘flag’ to denote a authenticated user.

Consecutively, our ‘members only’ pages will have something like this defined in their controller:

1
2
class MembersController < ApplicationController
before_filter :check_login_status

Where check_login_status is a method which will be evaluated before every action in this controller. The check_login_status will look something like:

1
2
3
4
5
6
7
8
9
class ApplicationController < ActionController::Base
protect_from_forgery
..def check_login_status
....if session[:valid] != "true"
......flash[:message] = "That is a 'member only' page, please login with valid credentials to access it"
redirect_to "/auth/login"
....end
..end
end

I’ve put up a demo application at http://replayattack.mocktest.net where you can play with a simple login based application which uses cookie based session store. You can try the replay attack (described below) on this application.

I use a simple Firefox extension – Live HTTP headers for evaluating and replaying the cookie. So, lets login at the application (demo/demo) and lets see what cookie we got. The headers for ‘/members/profile’ page sets this cookie.

Set-Cookie: _rails_demo_session=BAh7DUkiD3Nlc3Npb25faWQGOgZFRiIlZTBhZWRjZGE3M
GE5MTk5MGMyNTllMDU1ZWRkMGRkMDdJIhBfY3NyZl90b2tlbgY7AEZJIjFicFl4aUpuSURI
WkVhaE8rTVpuTDgrUWNvdzlwRnYzVWJhcEYwazBDRzM0PQY7AEZJIgp2YWxpZAY7AEZJ
Igl0cnVlBjsARkkiCW5hbWUGOwBGSSIRU2hpdmFtIFBhdGVsBjsARkkiCmVtYWlsBjsARkkiE3
NoaXZhbUBjbXUuZWR1BjsARkkiDXBhc3N3b3JkBjsARkkiEGNvbW1hY2tANzgyBjsARkkiDGN
yZWRpdHMGOwBGSSIINzgwBjsARkkiCmZsYXNoBjsARklDOiVBY3Rpb25EaXNwYXRjaDo6Rm
xhc2g6OkZsYXNoSGFzaHsGOgxtZXNzYWdlSSIxTG9naW4gU3VjY2Vzc2Z1bCEgV2VsY29tZSB
0byB0aGUgbWVtYmVyIGFyZWEGOwBGBjoKQHVzZWRvOghTZXQGOgpAaGFzaHsA–8033ab5323d8adfb80a32fa448a41a586138ee4a; path=/; HttpOnly

You can now see two distinct parts of this cookie: The base64 encoded session data followed by the delimiter – - followed by the HMAC (8033ab5323d8adfb80a32fa448a41a586138ee4a). If you use any base64 decoder and try to decode the session data part of the cookie (BAh7…. aHsA), you’ll see the serialized representation of the session hash:

{
I”session_id:EF”%e0aedcda70a91990c259e055edd0dd07I”_csrf_token;�FI”1bpYxiJnIDHZEahO+MZnL8+Qcow9pFv3UbapF0k0CG34=;�FI”
valid;�FI”    true;�FI”    name;�FI”Shivam Patel;�FI”
email;�FI”shivam@cmu.edu;�FI”
password;�FI”commack@782;�FI” credits;�FI”780;�FI”
flash;�FIC:%ActionDispatch::Flash::FlashHash{: messageI”1Login Successful! Welcome to the member area;�F:
@usedo:Set:
@hash{�

We can see the session variables highlighted in this semi-readable text. Can you change the value of ‘credits’ in the cookie and fool the server to accept that. Fortunately not, because the HMAC won’t match. But you can always store this cookie to replay it later. And here lies the problem.

Now we’ll logout from the application by clicking the ‘Logout’ link on the web application. The logout code is:

1
2
3
4
5
def logout
..reset_session
..flash[:message] = "You have been successfully logged out !"
..redirect_to "/auth/login"
end

This resets the session and generates a new session id. If you inspect the new cookie (post logout) in Live HTTP headers, you’ll no longer find those session variables.

Locate an old GET request in Live HTTP header and replay it

But what if you just replay that old cookie. In Live HTTP headers, all you need to do it to scroll up to reach the request which was made to a page before you clicked logout and “Replay it”.

As soon as you click that, BINGO !!! – you are logged in as a valid user.

This happened because the session[:valid] flag we are checking on server side to see if the user has already authenticated to the application is set in this cookie which obviously is of a time before users clicked ‘Logout’. This is a very serious issue. You need to worry if your applications’ login design is similar to one described here.
This will allow people to sniff your users cookie if they are on a public wireless (airports, coffee shops) and replay it to gain access to your application even when the real user has logged off the application.

You are safe from such an attack if:

  • You use SSL on your website. However, remember that SSL should be enabled for the entire site rather than for the login page only. For sites using SSL, there is no way an attacker can sniff plain text cookies and try to read/replay them.
    • Also make sure that you set the ‘secure’ cookie flag so that the cookie is never inadvertently transmitted on a http channel
  • You are not using cookie store to manage your sessions.

If you want to quickly migrate from cookie based session store to database based sessions, its actually very easy. However you may want to think about the performance impact it will have based on the way you’ve deployed your application.

In case you want to switch to database backed sessions, here is how to.

Hope this post helped you understand how cookie based sessions work and why and when you should not use CookieStore. Have fun doing the replays at http://replayattack.mocktest.net. Don’t mount other nefarious attacks on it please.