Wolfmans Howlings

A programmers Blog about Programming solutions and a few other issues

A Simple Challenge Response authentication scheme for Rails3

Posted by Jim Morris on Mon Nov 29 23:06:41 -0800 2010

UPDATE the following is for pre rails 3.1, there is an addendum at the end for rails 3.1+

I have ported my blog engine (that hosts this site), from Merb to Rails 3. You would expect it to be fairly simple as Rails 3 is meant to be the next version of Merb.

It turned out to not be too difficult, I have a few extra hurdles in that I use Sequel as the ORM and JQuery as the javascript and HAML as the rendering engine, all are pretty well supported by now.

The source code for the project is on github

One issue was authentication for the admin, I was using the Merb Salted User plugin, so initially I used the Rails3 built-in Digest Authentication, which worked very well until I deployed to my server which runs Passenger under Apache2. It seems Apache does not let the Authorization headers through to Passenger then to the rails app, so it doesn't work.

So I decided to use the simplest implementation I could think of that wasn't totally insecure.

There are several plugins for Rails3 that support various methods of Authentication, but all were overkill for this project. Basically only one person logs in as admin, me, and it enables a few admin features like the ability to delete comments (or spam), add new articles, and edit them.

In my Java server work, I usually use a simple Challenge/Response technique for authentication:

  1. the client asks to login
  2. the server sends a nonce
  3. the client combines the nonce with the hashed password
  4. the client sends this auth token to the server
  5. the server grabs the hashed password from the database
  6. the server applies the same combination of the nonce and hashed password, and compares them

This is well known scheme for authentication over unencrypted connections, where the password does not get transmitted in the clear and even the hashed password does not get transmitted. Specifically I use this scheme.

The implementation of this scheme using Javascript/JQuery and Rails is trivial.

First I only really want to load the javascript sha1.js and my auth.js when you ask for the login page, so I don't burden regular users with that extra download. Rails allows this by allowing a view to add stuff to the <head> section:

In the layout add...

In the login view page put...

the sha1.js is available from here

My auth.js is very simple...

for this very simple HAML login view...

This uses the highly acclaimed unobtrusive Javascript techniques which is everywhere these days.

It takes the SHA1 hash of the password the user entered, takes the SHA1 hash of the hashed password and the nonce and replaces the password field with that result. Then when posted to the server is sends this auth token instead of the password.

On the server in the users_controller.rb I have a login action which generates a unique random nonce, which gets embedded in the login form as a hidden field, and stored in the session. Then when the login form submit button is clicked the javascript does its stuff and the form is posted to the server, and the action attempt_login is executed (thanks to routing).

All I do is set a session value to say it is logged in, this is used in the other controllers to check the authentication protected actions, and in the views to enable admin links.

The User model has a class method that allows you to generate a hashed password via the console...

The migration for the user table is...

I add a helper in application_helper.rb so the views can check if the user is logged in or not, thereby enabling an admin buttons and links.

In application_controller.rb I have a method which is used by any action to test if the user is authenticated, this is usually done in a before_filter...

Pretty simple stuff, but very effective.

Keen security experts will see several weaknesses though, but I find them acceptable given the low risk of a Blog.

  1. The Rails3 default session is stored on the client in the cookie, it is cryptographically signed, but if someone were able to get the key or figure out a way to sign the cookie, they could easily set the logged_in session or set the nonce to a known value. This can be mitigated by using the session store in the database or on the server.

  2. All cookie based authentication schemes are subject to session hijacking techniques.

  3. Key loggers could grab the clear text password.

  4. It is possible to capture the nonce and the response if sent over a non-https connection, and if the password is a dictionary word a brute force attack could be used to find the password.

  5. If sessions are stored on the client (the default) then I think if someone can steal the entire cookie just after authentication they can simply replay it to login, this agsain is mitigated by having the session data stored on the server.

And probably several other attacks, you could get paranoid just thinking about them all :)

UPDATE for Rails 3.1+

Most of this is applicable to rails 3.1, except the auth.js and sha1.js need to be moved to lib/assets/javascripts, and add this line to the start of auth.js

//= require sha1

This will precompile the two files and make them available in public/assets for production.

Then modify the login view page to only include auth.js...

- content_for :head do
  = javascript_include_tag 'auth'

You also need to add this line to config/environments/production.rb otherwise auth.js does not get added to public/assets in production.

# Precompile additional assets (application.js, application.css, and all non-JS/CSS are already added)
config.assets.precompile += %w( auth.js )

Posted in Rails  |  Tags authentication,javascript  |  no comments


(leave email »)