Ruby on Rails Password Hashing Module

This is a very simple password module that is also easy to use. Simpy place it in /lib inside your Ruby on Rails application and start protecting your passwords today. This code uses a long hash, and creates individual salts for each password stored. It should be very computationally expensive for someone to crack every password in your database, were they to fall into the wrong hands. Of course, if your database is in the wrong hands, you probably have bigger problems. But even some large sites have been caught storing passwords in plain text.

Ruby Password Hashing Code

require digest/sha2

# This module contains functions for hashing and storing passwords
module Password

  # Generates a new salt and rehashes the password
  def Password.update(password)
    salt = self.salt
    hash = self.hash(password,salt)
    self.store(hash, salt)
  end

  # Checks the password against the stored password
  def Password.check(password, store)
    hash = self.get_hash(store)
    salt = self.get_salt(store)
    if self.hash(password,salt) == hash
      true

    else
      false
    end
  end

  protected

  # Generates a psuedo-random 64 character string

  def Password.salt
    salt = ..
    64.times { salt << (i = Kernel.rand(62); i += ((i < 10) ? 48 : ((i < 36) ? 55 : 61 ))).chr }
    salt
  end

  # Generates a 128 character hash
  def Password.hash(password,salt)
    Digest::SHA512.hexdigest("#{password}:#{salt}")
  end

  # Mixes the hash and salt together for storage
  def Password.store(hash, salt)
    hash + salt
  end

  # Gets the hash from a stored password
  def Password.get_hash(store)
    store[0..127]
  end

  # Gets the salt from a stored password
  def Password.get_salt(store)
    store[128..192]
  end
end

HTML code generated by vim-color-improved v.0.3.2.Download this code: password.rb

Usage

Using the the password module is simple. All you need to do is save the file above as “password.rb” in the lib directory of your rails project. Then require_dependency “password” in your application.rb. Once that is done you are free to use the functions in any controller.

 

Example

application.rb

# Filters added to this controller will be run for all controllers in the application.
# Likewise, all the methods added will be available for all controllers.
require_dependency password

class ApplicationController < ActionController::Base
end

HTML code generated by vim-color-improved v.0.3.2.

account_controller.rb

This is an example account controller.

class AccountController < ApplicationController
  layout standard
  before_filter :login_required, :except => [:login]

  def login
    case request.method
      when :post
      if session[:user] = User.authenticate(params[:user_login], params[:user_password])
        flash[:notice]  = Login successful

      else
        session[:user] = nil
        flash.now[:notice]  = Login unsuccessful
        @login = params[:user_login]
      end

    end
  end

  def logout
    session[:user] = nil

  end

  def welcome
  end

end

HTML code generated by vim-color-improved v.0.3.2.

user.rb

This is the model for the user class. As you can see, password checking against a hashed password is very simple here. Authenicating the user returns a User object, which is stored in the session[:user] variable in the controller above.

class User < ActiveRecord::Base

  # Checks login information
  def self.authenticate(nick, pass)
    user = find(:first, :conditions => ['nick = ?',nick])

    if Password::check(pass,user.password)
      user
    else
      return false
    end
  end

  protected

  # Hash the password before saving the record
  def before_create
    self.password = Password::update(self.password)
  end

end

HTML code generated by vim-color-improved v.0.3.2.

11 Responses to “Ruby on Rails Password Hashing Module”

  1. Andy Yates Says:

    Glad to see that there’s some more modules hanging around where people are taking password hashing seriously. It’s nice to see someone actually think:

    * Salts … bloody good idea
    * Random salts … even better
    * Hashing using something a bit stronger than MD5 (not saying that MD5 isn’t good … just SHA512 is better)

    However there are a few points with your algorithm that you may want to consider in future versions (I apologise if there is anything that Rails does automagically that I’m mentioning here)

    * You’re only hashing the password once. Now even though SHA512 is a good hashing algorithm you may want to consider passing it through the algorithm a few more times. I think the RSA recommended amount is 1,000 times! Seems a bit excessive but as you’ve mentioned that this procedure is a very small amount of the entire lifetime of a user on a website so it’s not that bad to CPU load this section

    * There is no normalisation of user input. Imagine a situation where a user enters a password which contains a non-ASCII character on a Windows 2000 box & then attempts to re-login on a Linux box. There’s a risk that the encodings both OSs use could cause problems with byte representations & digesting. It would be worthwhile to ensure that you’re in UTF8 or some kind of universal character set

    * Some databases can have problems storing non-ASCII Strings (Oracle springs to mind). So before storage I’d encourage using BASE64 encoding of the byte digest and storing this instead.

    However the module is very good in its current state & something which really is a step in the right direction :)

  2. Mikkel Riber Says:

    Hi.

    Love your password lib - it saved me for some headache.

    At the moment we are 2 developers working on a project, and i have a database with users and hashed passwords.

    Is it true that a hash string, generated on my server won’t work on his server? We entered the exact same password hash into his user db, but it wouldn’t authenticate - if we generated a new hash on his server everything worked fine.

    Best Regards
    Mikkel Riber

  3. de tomKronieken » Blog Archive » links for 2008-01-23 Says:

    [...] Ruby on Rails Password Hashing Module Shared with shareomatic.com [...]

  4. Gerrad Fase Says:

    Where can I download this library - is it an available Gem on RubyForge?

  5. Zachary Fox Says:

    I’ve updated this page so that you can download the password.rb file. Just look at the bottom of the source code for a link to download it.

  6. Gerrad Fase Says:

    Got it! Thanks a ton - now to put it to work.

  7. Thomas Says:

    A few points: Your code gave me a little headache, since it uses dots (.) where ruby expects quotes (’ or “), but this was solved very fast.

    Second, in User.rb, the password will only be hashed, when the user is created. If you change the password, it just stores the plain password.

    So instead of using before_create you could just overwrite the assignment method:

    def password=(pass)

    write_attribute(:password, password = Password::update(pass))

    end

    Now the password will always be hashed.

    Best Regards
    Thomas

    P.S.: be sure to remove before_create, otherwise your password will be hashed twice and you cannot login.

  8. Thomas Says:

    I have to comment myself:

    the assignment-method works, but the cost is, you cannot validate the password (e.g. against a minimum length), because validations uses the hashed value of the password. A better approach is:

    protected
    def before_save
    self.password = Password::update(self.password) if self.password_changed?
    end

    But even here, you have to validate like this:

    def validate
    if self.password_changed?
    errors.add(”password”, “at least 10 characters”) if self.password.length < 10
    end
    end

    since all other validation-methods will use the hashed-version of the password, when you e.g. change some other attribute but not the password.

    Best regards
    Thomas

  9. Zachary Fox Says:

    @Thomas

    Good points. These are very simplified examples of code that is in a CMS I wrote. I actually use a before_save method to check the values of password and password_verification fields (which are submitted from a form), along with additional validations to ensure that the password is valid (length, complexity, etc…)

    For multiple reasons, I haven’t provided complete examples, but enough that you can work with the password.rb lib.

    I’m glad you found it useful, and I’ll look into the source files to see why the quotes aren’t working properly.

  10. fophillips Says:

    Is this code released under a specific license? I would like to use it in my project under the AGPL.

  11. Hugo Peixoto Says:

    @Thomas
    Instead of before_create, you could use after_validate_on_create.

Leave a Reply