Jun 06 2008
restful_authentication howto, step-by-step (part 2)
Picking it up were we left it on restful_authentication howto, step-by-step (part 1) the second article of this series is a hands on example on how to use the restful_authentication plugin.
Things that will be covered include:
- remove the need of a login
- the use of an activation email, the application will require it’s users to activate their accounts upong sign up.
- howto get rid of the remember me functionality (just in case you don’t need it).
- howto strengthen a bit the default security of the framework.
If you started a blank application with the first series, you can seamlessly continue with the instructions of this post from were we left it on the first part of this howto. Otherwise, you can grab the code of restauthz application, a small rails application that I have created and that can be used as a proof of concept out of the box. Let’s get this thing going.
Before we start, just a gentle reminder, be sure to include AuthenticationSystem in ApplicationController:-
include AuthenticatedSystem # Filter the password and password_confirmation # fields from the log files filter_parameter_logging :password, :password_confirmation
no login, just email
The first step is to remove the login field from the User migration, this will ensure that we do not use it in the code
We also need to remove the revelant validatiors in the User model. In addition to this, some changes to the authenticate are required:-
def self.authenticate(email, password) u = find_in_state :first, :active, :conditions => {:email => email} # need to get the salt u && u.authenticated?(password) ? u : nil end
We also need to update the call to this function in the SessionsController (line 9):-
self.current_user = User.authenticate(params[:email], params[:password])
And that’s it. It wasn’t that difficult, was it?
email activation
The only thing that we are going to tweak is the templates provided by restful_authentication.
In ./app/model/user_mailer.rb you can modify the email headers such as the subject and from address. The body of the emails is located under ./app/views/user_mailer/.
The system sends to the users two emails, one after signup (this one contains the activation link) and one once the user has activated the account.
By default the templates contain the newly created user’s password, which is something that is controversial to say the least. I decided to get rid of the password, but this depends on your needs more than anything else.
remember me
Another feature that is application dependant is the use of a remember me functionality: a check box in the login form that would cause the application to store an authentication token in the user’s cookie so the next time the user visits the site does not have to authenticate again. I decided to nail down this example to the very basics, so no remember me functionality in this instance.
This can be accomplished by making some modifications to the AuthenticatedSystem#current_user function:
def current_user #@current_user ||= (login_from_session || login_from_basic_auth || login_from_cookie) unless @current_user == false # only session based login for the time being @current_user ||= login_from_session unless @current_user == false end
In the previous code, the @current_user variable is only set through the session, no HTTP Basic (careful if you have ActiveResource clients) or remember me cookie.
security tweaks
password policy
A strong password policy is enforced by means of rails’ validate_format_of. In the User model:-
class User /^(?=.*\d)(?=.*([a-z]|[A-Z]))([\x20-\x7E]){8,40}$/, :message => 'chosen is not complex enough!' validates_format_of :email, :with => /^([a-zA-Z0-9_'+*$%\^&!\.\-])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9:]{2,4})+$/, :message => 'field does not look like an email.' [...] end
In the code above we match both the email and the password against regular expressions to verify the syntax.
The regular expression for the password was taken from Using Regular Expression in Ruby on Rails — Regexp for Password Validation:-
Lets say we have to implement the following validations to validate a password:
- Password should contain atleast one integer.
- Password should contain atleast one alphabet(either in downcase or upcase).
- Password can have special characters from 20 to 7E ascii values.
- Password should be minimum of 8 and maximum of 40 cahracters long.
password with salt & pepper
Storing a password in plaintext may result in a system compromise (OWASP).
Conveniently enough, restful_authentication uses a hash function to protect user passwords: the SHA-1. It also uses a salt. Here are however two tricks to increase the security of the default setup:
First we are going to hash the password with salt and pepper. The salt is specific to each user and will be stored in the database along with the user’s hashed password. If an attacker can compromise the database, they would have access to both the salt and the hash and brute force attacks could be mounted by using custom scripts or rainbow tables. The trick here is to add a second component, the pepper. The pepper is another random string that will be used to add some extra entropy to the password hashing process. In our implementation the same pepper will be used for all the users and it will be stored in the code. If an attacker gains access to the database, no successful brute force attack can be mounted without knowing the pepper. If an attacker gains access to both the database and the code…
You can easily generate your pepper using something like this in code in irb:-
require 'digest/sha2'
s = ''
chars = ("a".."z").to_a + ("A".."Z").to_a + ("0".."9").to_a
1.upto(4) { |i| s << chars[rand(chars.size-1)] }
s += Time.now.to_s
1.upto(4) { |i| s << chars[rand(chars.size-1)] }
Digest::SHA256.hexdigest(s)
=> “9fa4c6519da9d2121bc42be1d63813f591bba8ece5b753e6ceefed00f15e5342″
The random character generation was taken from generate a random password.
And second, we can upgrade the system to use the more secure SHA-256 both for the hashed password and the salt. We will keep SHA-1 for the sake of variety
In order to accomplish this, some modifications are needed to the ./app/model/user.rb. First we need to include the required files:-
# sha1 for activation code. sha2 for the passwords require 'digest/sha1' require 'digest/sha2'
Then the password encryption function:-
# We are using both, salt and peper to hash the # password. The new password hash uses # SHA2.hexdigest def self.encrypt(password, salt) pepper = '9fa4c6519da9d2121bc42be1d63813f591bba8ece5b753e6ceefed00f15e5342' Digest::SHA256.hexdigest("--#{salt}--#{password}--#{pepper}--") end
And the salt generation function:-
# The salt is also created using SHA256 def encrypt_password return if password.blank? self.salt = Digest::SHA256.hexdigest("--#{Time.now.to_s}--#{email}--") if new_record? self.crypted_password = encrypt(password) end
In order to store the new hash, we need to increase the length of the salt and crypted password fields in the database. This can be done in the migrations file:-
t.column :crypted_password, :string, :limit => 64 t.column :salt, :string, :limit => 64
summary
So that was it, the restauthz application should cover all the needs to get you started with restful_authentication. There is still room for improvement, think the I forgot my password functionality, or a facility for your users to change their passwords, but that is definitely another story ![]()




