User Authorization in Rails
Posted by Bart ten Brinke Wed, 22 Oct 2008 09:17:36 GMT
A lot of rails apps use some form of user login. Usually a user identifies itself by entering a username/password combination into a form. The application will then check the combination and use the session of the user to store which user authenticated for that session.
Plugins like acts_as_authenticated give a nice starting point, but must applications write authorazation themselves as it is quite simple to do in Rails. If you have done this too, then here are a number of security points which such login process should take into account.
Brute forcing
Rails apps are usually tweaked to respond to requests as quickly as possible. If a response is slow, it will block my mongrel! This makes Rails apps a very good partner for the brute forcing of passwords. Action against this is surprisingly simple. You can count the number of incorrect login attempts. If this becomes more then 10, let the user enter a captcha. Or really degrade the login by rejecting every login attempt with a 403, when the last incorrect login attempt was less then two or three seconds ago.
HTTP login posting
Many sites offer a login box on their front page. This is a very user friendly and I would be the last person to tell you not to do this. However, this often means that you are letting a user authenticate over HTTP, which is not very secure. Anyone snooping the traffic of that user will see a plaintext username and password being posted to your site. Poke around with Webscarab or Firebug in Firefox to pick up on these things easily.
Solutions are again easy. If you have a HTTPS part of your site, make sure the login form posts to it. If you do not have HTTPS, you can encrypt the password in the clients browser with javascript via a public/private key combination. A lot of good examples of this can be found all over the internet.
Session hacking
At the moment Rails supports two types of sessions. It can reside serverside in a file or database and clientside in the cookie of the client.
We will first discuss the server side session, as this was the default before Rails 2.1. When a user connects to your website, he will automatically receive a unique session id. This is is the only thing your application can use to distinguish between unique users and their requests. The default session ids in Rails are generated correctly, so that is very hard for someone to guess ids of other users using your application. But if a hacker does acquire a session id of another user, Rails will not offer you any protection by default. And there actually quite a few ways for a hacker to obtain session ids of other users. The two most commonly used being: Cross Site Scripting exploits (XSS) and code injection.
Why is this a problem? Well if an attacker shares a session id with another user, and that user logs in, the attacker will share their session. This means that the attacker will have the same privileges as the user. Countering this can be a bit of a hassle, but as every application is sure to have a XSS exploit somewhere, it is a hole that is important to plug.
First you need to reset sessions after a logon. This will counter any creatively crafted URL attacks. Secondly it is a good idea to fixate your sessions on the ip used in the login attempt and you also should take a look at sesion expiration.
Clientside sessions have the same problems, so don't count on them as being the ultimate solution.
SQL injection
Initially I wanted to leave this out, i as the problem is still so common, I decided to have it here anyway. Rail does provide you with SQL injection protection, but you still have to use it correctly in order to be safe.
User.find(:conditions =>
["username = #{username} AND password = MD5(#{password})"])
User.find_by_username_and_password(username, SHA(password))
They both work, but the first one can be easily exploited through SQL injection.
Conclusions
This will not magically fix all your user authorization problems, but it should point you in some interesting directions. Want to read more? Here are a few good places to start reading.
http://guides.rails.info/securingrailsapplications/security.html http://www.rorsecurity.info

"The first two work, but can be easily exploited through SQL injection." The first is definitely broken, but in what way is the second open to SQL injection? I am pretty sure that ActiveRecord escapes this form correctly.
I agree. I think Bart made a mistake there :)
Strange, I really thought the lack of quotes caused an exploit possibility there. Can it be that this has been fixed in activerecord recently?
It seems that the guys at Twitter did not read my post :) http://tech.slashdot.org/article.pl?sid=09%2F01%2F08%2F1531220