Topic: HowTo - Advanced MD5 database authentication from scratch in 20 steps.

HowTo - Advanced MD5 database authentication from scratch in 20 steps.

This is an advanced md5 database authentication from scratch in 20 Steps Tutorial. is not meant to be the replacement of any of the Authentication plugins. The only purpose is to ilustrate
an easy way to put security into your rails application.

This is the advanced Tutorial based on:
[ Very simple authentication from scratch in 15 steps ] That is the reason many of the steps will look pretty similar ;-)
http://railsforum.com/viewtopic.php?id=15130

1.- Create your Rails Application
rails mylogin -d mysql


2.- Change to your new application directory.
cd mylogin

3.- Edit the config/database.yml file with your own settings.

Tip For windows users only. Open a windows explorer in your current path just like textmate ;-)
     explorer /e, /root, c:\railapps\mylogin
Tip for Linux users. Check on customize gedit to look just like textmate ;-)
     http://blog.ubrio.us/gnome/making-gedit … for-linux/
Tip for Mac users... never mind they don't need tips LOL

Anyway setup your config/database.yml file with your own db settings.

4.- Create your databases.
Run 1 of the following rake tasks to create the database(s) of your application.

If you want to create all the database use the rake task:
     rake db:create:all
if you just want to create the development database for now just run:
     rake db:create

5.- Once you create your database(s). Lets run some scaffold generators to make our life easier....
This is a simple Project list application with a Users table which are allowed to authenticate to the system, the sessions controllers is just to let us keep track of the

authentication.

ruby script\generate scaffold Project title:string body:text
ruby script\generate scaffold User username:string password:string
ruby script\generate controller sessions login logout

6.- Ok let the games begin... lets run our migrations. and create our tables into the DB.
rake db:migrate

7.- Lets Launch the web server and add a couple projects & a couple users...
$ruby script\server

Open your browser in
http://127.0.0.1:3000/projects.

Use the web interface to add 2 Projects or via console if you like it better.:
Project 1
Project 2

Lets add 2 users:
http://127.0.0.1:3000/users
User: foo Password bar
User:admin Password admin

I know what you have in mind... don't worry we will change those plain text passwords into MD5 hash.  This is just to make it easier to see in this tutorial !!!.

8.- So far It's all pretty basic Rails stuff. Let's spicey up our Tutorial with the "User.authenticate(username,password)"
Lets add the database authentication function:: User.authenticate(username,password)

Edit the User Model located In the [ app/models/user.rb ] :

class User < ActiveRecord::Base
  def self.authenticate(login, pass)
    return false if login.nil? or pass.nil?
    find_by_username_and_password(login,pass)
  end
end

This is a method which can be accessible from the console when you call:
User.authenticate("foo","bar") => Then looks to the Users table and if it founds a username called "foo" with password "bar" returns the record, otherwise returns nil.

>ruby script\console
Loading development environment (Rails 2.0.2)
>> User.authenticate("foo","bar")
=> #<User id: 1, username: "foo", password: "bar", created_at: "2008-04-14 17:26:53", updated_at: "2008-04-14 17:26:53">

9.- Let's implement "Virtual Attributes" for the User Model: [ app/models/user.rb ]
Ryan Bates did a great job on: [ http://railscasts.com/episodes/16 ]

Lets add the getter & setter for mypassword:

 def mypassword
   password.to_s
end

def mypassword=(pass)
   self.password= MD5.new(pass).to_s
end


Since we going to use MD5 we must declare it as required with:
require 'MD5'

The final script looks like this:

require 'MD5'
class User < ActiveRecord::Base

#  validates_length_of :username, :within => 5..40
#  validates_length_of :password, :within => 5..40
#  validates_presence_of :username
#  validates_uniqueness_of :username

def mypassword
   password.to_s
end

def mypassword=(pass)
   self.password= MD5.new(pass).to_s
end

  def self.authenticate(login, pass)
    return false if login.nil? or pass.nil?
    find_by_username_and_password(login,pass)
  end

end


At this point is required to restart the Web server to take the changes of the require 'MD5' of the User Model.

10.- Lets delete the public/index.html page
rm public/index.html

11.- Edit the config/routes.rb with a default route, resources for the sessions, home_path, login and logout !!!

ActionController::Routing::Routes.draw do |map|
  map.resources :users
  map.resources :projects
  map.resources :sessions

  map.home '', :controller => 'projects', :action => 'index'
  map.login 'login', :controller => 'sessions', :action => 'new'
  map.logout 'logout', :controller => 'sessions', :action => 'destroy'
...


12.- Let's secure the Index Projects page for the New Project: [ app/views/projects/index.html.erb ]

<h1>Listing projects</h1>

<table>
  <tr>
    <th>Title</th>
    <th>Body</th>
  </tr>

<%= link_to 'New project', new_project_path %>

<% for project in @projects %>
  <tr>
    <td><%=h project.title %></td>
    <td><%=h project.body %></td>
    <% if admin? %>
    <td><%= link_to 'Show', project %></td>
    <td><%= link_to 'Edit', edit_project_path(project) %></td>
    <td><%= link_to 'Destroy', project, :confirm => 'Are you sure?', :method => :delete %></td>
    <% end %>
  </tr>
<% end %>
</table>

<br />
    <% if admin? %>
<%= link_to 'New project', new_project_path %>
    <% end %>


The ifadmin? method will allow to see and access the administrative links only if the admin? method returns true.
At this point your web server will show undefined method `admin?'. but It's ok we are about to define it.

13.- Lets declare this important admin? method in the Application Controller to make it accessible for all the models.
[ app/controller/application.rb ]

class ApplicationController < ActionController::Base
  helper :all # include all helpers, all the time

Lets add the protected methods...

class ApplicationController < ActionController::Base
  helper :all # include all helpers, all the time

  helper_method :admin?
 
   protected
   def authorize
     unless admin?
       flash[:error] = "unauthorized access"
       redirect_to home_path
       false
     end
   end
 
  def admin?
    username = session[:username]
    password = session[:password]

      if User.authenticate(username, password)
        true
      else
        false
      end

  end


Now if you refresh your projects page, will se the Projects without the administrative links. But still accessible via http://127.0.0.1:3000/projects/show/1 until we apply our

before_filter.

14.-  In the projects controller file add a before_filter method to perform the authorize validation previously declared
[ app/controller/projects_controller.rb ]

class ProjectsController < ApplicationController
  # GET /projects
  # GET /projects.xml
....

Replace with:

class ProjectsController < ApplicationController
  before_filter :authorize, :except => :index

  # GET /projects
  # GET /projects.xml
....


Now try to access the http://127.0.0.1:3000/projects/show/1 And will be redirected to http://127.0.0.1:3000/projects Pretty Cool Huh !!!.

15.- Lets create 2 methods on the sessions controller to perform the login authentication:
[ app/controller/sessions_controller.rb ]

class SessionsController < ApplicationController

  def login
  end

  def logout
  end
end


Replace with:

class SessionsController < ApplicationController

def create
      session[:username] = params[:username]
      session[:password] = MD5.new(params[:password]).to_s

      # flash[:notice] = "Successfully logged in"
      redirect_to home_path
  end

  def destroy
    reset_session
    flash[:notice] = "Successfully logged out"
    redirect_to login_path
  end

#  def login
#  end

#  def logout
#  end

end


16.- I'ts time to create the Login page. Rename the file called login.html.erb to new.html.eb.
$mv app/views/sessions/login.html.erb app/views/sessions/new.html.erb

17.- Working with the Login/Logout Views
Edit this app/views/sessions/new.html.erb:

<h1>Sessions#login</h1>
<p>Find me in app/views/sessions/login.html.erb</p

Replace with:

<center>
  <% form_tag sessions_path do %>
    <p>
    Username <%= text_field_tag :username %>
    Password <%= password_field_tag :password %>
    <%= submit_tag "Login" %>
    </p>
  <% end %>
</center>

Now for the Logout: Edit the app/views/sessions/logout.html.erb

<h1>Sessions#logout</h1>
<p>Find me in app/views/sessions/logout.html.erb</p

Replace with:

<center>
  <h3> Successfully Logout </h3>
</center>

18.- Create an application layout in [ app/views/layouts/application.rb ] This will let us in a very easy way to put a very simple navigation for your application.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
       "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
  <meta http-equiv="content-type" content="text/html;charset=UTF-8" />
  <title>My Rails SSO: <%= controller.action_name %></title>
  <%= stylesheet_link_tag 'scaffold' %>
</head>
<body>

<p style="color: green"><%= flash[:notice] %></p>
<hr size="1" color="#000000">
<%= yield  %>
<hr size="1" color="#000000">
<% if admin? %>
<%= "Logged as: " + session[:username] %> <br>
<%= link_to "Logout", :controller => 'sessions', :action => 'destroy' %>
<% else %>
<%= "Not Logged in: " %> <br>
<%= link_to "Login", new_session_path %>
<% end %>
<hr size="1" color="#000000">
<center>
[ <%= link_to "Users", :controller => 'users', :action => 'index' %> ] &nbsp
[ <%= link_to "Projects", :controller => 'projects', :action => 'index' %> ] &nbsp

</center>
<hr size="1" color="#000000">
</body>
</html>


=> To make it active delete or rename the projects & users layouts. You can refresh your web applications and see the application layout navigation working.

19.-  Update the User views for New & Edit pages:
[ app/views/users/new.html.erb ] && [ app/views/users/edit.html.erb ]
Change the name of the :password to :mypassword => This will use the Virtual Attribute declared in the user model.

  <p>
    <b>Password</b><br />
    <%= f.text_field :mypassword %>
  </p>

This change will activate the feature of create MD5 everytime a user/password is created or edited.
Open your browser at http://127.0.0.1:3000/users and create a couple more users... see the MD5 Hash ;-)

Try with the previously created users once you update the current values the application will update will update with the hash of the password.

20.- That's all folks, try to login with one of the users you have in your users table.
You should be able to click on the login link in your application and login with some of your users.

Once you logged into your application you should be able to see the links to administrer the Projects table. The navigation menu will show you as which user you are

logged in.

In case you logout from the application. The Projects page will show only the list of Projects without administrative links. All the administrative links will not be accessible at

this point smile

CONCLUSION:
User.authenticate give us a big range of choices to authenticate users from our Rails Application. In this case from a table in a Database, but could it be from another

Single Sign On Server, Web Services or something else.

Enjoy to learn a little bit about more rails authentication, because I like do things from scratch, then when I learn can start using more advanced plugins ;-)

The more I learn about Ruby & Rails the more I want to use it for everything smile

TODO:
There is a little bug in the [ Login/Logout ] Link Navigation. The properly link will show but if you edit a User or a Project the link Logout
[ <%= link_to "Logout", :controller => 'sessions', :action => 'destroy' %> ] will point to:
[ http://127.0.0.1:3000/sessions/1 ]  instead of [ http://127.0.0.1:3000/logout ]

In case you click on this logout link:
Unknown action
No action responded to show

I have a couple ideas on how to fix this... the easiest solution & ugly solution will be to hardcode the link to Logout !!!.

Alternavite ugly link to logout in [ app/views/layouts/application.rb ]

[ <a href="http://127.0.0.1:3000/logout" >Logout</a>] &nbsp 
<%= link_to "Logout", :controller => 'sessions', :action => 'destroy' %>

But I still like to keep it secure, so the next idea is to do something with the routes, but I guess that will be subject for another post.

Din00z
Brains R Like Books only work when they R Open.

Re: HowTo - Advanced MD5 database authentication from scratch in 20 steps.

Wheres the salt?

http://en.wikipedia.org/wiki/Salt_(cryptography)

Last edited by jbartels (2008-04-15 12:26:07)

Re: HowTo - Advanced MD5 database authentication from scratch in 20 steps.

Spyce with Salt & Pepper your MD5 Authentication.

Good question. Here is a suggestion:

First lets modify the User model [ app/models/user.rb ] and change the simpe MD5 to a more elaborated text string with a Prefix & Sufix. ( Salt & Pepper )
Prefix = "s3cr3t_pr3f1x"
pass = "**********"
Suffix = "s3cr3t_suf1x"

Lets modify the setter of the mypassword function to:

 def mypassword=(pass)
#   self.password= MD5.new(pass).to_s
   salt_pass = "s3cr3t_pr3f1x" + pass + "s3cr3t_suf1x"
   self.password= MD5.new(salt_pass).to_s
end

This will create the MD5 of the string enter as a password in the Login form "******"  with the prefix: "s3cr3t_pr3f1x" and the sufix: "s3cr3t_suf1x"

Everything is good, but this is not the only change we also need to update our Sessions Controller [ app/controllers/sessions_controller.rb ] in order to keep the properly MD5 of the (Salt+Password+Pepper)

class SessionsController < ApplicationController

 def create
      session[:username] = params[:username]
#      session[:password] = MD5.new(params[:password]).to_s
      session[:password] = MD5.new("s3cr3t_pr3f1x" + params[:password] + "s3cr3t_suf1x").to_s

      # flash[:notice] = "Successfully logged in"
      redirect_to home_path
  end
......


This change will generate the exactly same string created by the mypassword setter with the salt & pepper. and let you login and keep the hash password not so simple.

Note: Keep in mind that previously created users need to be edited & updated in order to update the new MD5 hash password with the ( Salt & Pepper ).

This is just one way to solve it, perhaps not the most appropiate or elaborated, just to get you the idea of how everything works and you can take it from here,

Looking online found: http://pbeblog.wordpress.com/2008/02/12 … sing-salt/
Really like the idea of:

    $password = "banana"
    $salt = sha1(md5($password));
    $password = md5($password.$salt);

Which is very possible, but again this is just to get your the basics.

PS => The only thing that I would change here is to avoid dupplication [DRY]. Maybe store the (salt & pepper) in a single method will be more appropiate rather than coded into 2 files. Maybe is a good experiment for the user to do it ;-)

Din00z

Re: HowTo - Advanced MD5 database authentication from scratch in 20 steps.

Having both salt and pepper is a bit redundant, but I won't fight it. It amuses me. tongue

Salt should also be randomly generated per user whenever the password is changed. Your fixed string example illustrates how to use it and it shouldn't be too hard for the user to generate a new random string as a salt whenever the password is changed.

I like the second method as well, but I don't have the knowledge of cryptosystems to prove that its secure one way or the other. The idea of salt is to introduce some randomness into your password hashes, SHA and MD5a are hash functions and therefore never random.

Another option may be to use your applications cryptographic secret.The one generated by `rake secret` and stored in config.action_controller.session[:secret] . The downside to that is that it s consistent to your entire application and not per user.

The whole idea behind hashing and salting passwords is to prevent accidental discovery of passwords, and if that does occur to limit the range of passwords discovered.

Now I'm off to go re-read Applied Cryptography for the 3rd time. I keep forgetting what I learned from that book.

Re: HowTo - Advanced MD5 database authentication from scratch in 20 steps.

Great tutorial!

For some reason, "require 'MD5'" doesn't work for me. I'm not sure why I got this problem, but just incase anyone else is experiencing it:

change require 'MD5' to require 'digest/md5'
AND
change self.password= MD5.new(pass).to_s to self.password= Digest::MD5.hexdigest(new(pass).to_s)

Re: HowTo - Advanced MD5 database authentication from scratch in 20 steps.

May I can help you look thish
<%= link_to 'New project', new_project_path %>

<% for project in @projects %>
  <tr>
    <td><%=h project.title %></td>
    <td><%=h project.body %></td>
    <% if admin? %>
    <td><%= link_to 'Show', project %></td>
    <td><%= link_to 'Edit', edit_project_path(project) %></td>
    <td><%= link_to 'Destroy', project, :confirm => 'Are you sure?', :method => :delete %></td>
    <% end %>
  </tr>
<% end %>
</table>


______________________________________________


Download keygen    from this site.

Last edited by Bigrad (2009-10-14 17:49:00)

Re: HowTo - Advanced MD5 database authentication from scratch in 20 steps.

Thank you I'm starting to understand why Ruby on Rails is better to use than HTML

Re: HowTo - Advanced MD5 database authentication from scratch in 20 steps.

Andrew11 wrote:

Thank you I'm starting to understand why Ruby on Rails is better to use than HTML

Got it quite slightly. Will  you explain?

Last edited by gregorv (2012-06-19 09:54:54)