If you'd like to skip coding this up or are having issues, you can find the application source code here.
To start at the code which represents the completion of Parts 1-5, do the following:
$ git clone git://github.com/cktricky/attackresearch.git
$ cd attackresearch/
$ git reset --hard 3c3003d0b087d64f60446f74bcb2deedca60691f
$ bundle install
$ rake db:create db:migrate unicorn:start
=========== Part 5 Code and Walkthrough ==============
The first thing we need to do in this post is correct a mistake in the last post.
Type the following to change the model "Users" to it's singular form "User". If you don't, it will cause problems with Rails routing.
$ rails d model Users
$ rails g model User first_name:string last_name:string email:string password_hash:string password_salt:string admin:boolean
$ rake db:drop
$ rake db:create
$ rvmsudo rake db:migrate
Also, a small but important detail, please ensure within your Gemfile you change:
gem 'bcrypt-ruby', '~> 3.0.0'
gem 'bcrypt-ruby', '~> 3.0.0', :require => 'bcrypt'
Now........back to the series. So, we last left off where a login page was visible when browsing to your site but it didn't really do anything. Time to rectify that.
Within the Sessions controller, a create method was defined and in it we called the User model's method, "authenticate". We have yet to define this "authenticate" method so let's do that now.
|Located at /app/models/user.rb|
me = User.new
me.email = "email@example.com"
me.password = "test"
me.password_confirmation = "test"
me.save <~ at this point, any "before_save" methods get called
So when you see something like user = User.new and user.save you know that the encrypt_password method will be called by Rails prior to saving the user data because of the "before_save" definition on line 4.
Now we have to add a few more things:
validates_presence_of :password, :on => :create
These are basically Rails validation functions that get called when attempting to save the state of an object that represents a User. The exception being "attr_accessor", which is a standard Ruby call that allows an object to be both a getter & setter.
Okay, now let's see what it looks like.
Alright, so now we have a login page that does something but we need to create users. For this application's purpose, we are going to allow user's to signup. Let's provide a link for this purpose on the login page and even further, let's create a navigation bar at the top. We want this navigation bar visible on every page visited by the user. Easiest way to do that is to make it systemic and place it within the application.html.erb file under the layouts folder. Unless overridden, all views will inherit the properties specified in this file (navigation bar, for example).
|Located at /app/views/layouts/application.html.erb|
Without explaining all of Twitter-Bootstrap, one important thing to note is the class names of the HTML tags (ex: <div class="nav">) are how we associate an HTML element with a Twitter-Bootstrap defined style.
The logic portion, the portion that belongs to Ruby and Rails, are Lines 13 -18. Effectively we are asking if the user (current_user) visiting the page is authenticated (exists), if they are (do exist), show a link to the logout path. Otherwise, render a login and signup path link.
You are probably wondering where link_to and current_user come from. Rails provides built-in methods and you'll notice, in the views, they are typically placed between <%= and %>. So, link_to is a built in method. However, current_user is defined by us within the application controller and is NOT a built-in method.
|Located at /app/controllers/application_controller.rb|
The next thing we need to do now is actually make the signup page. First, let's modify the attributes that are mass assignable via attr_accessilbe in the user model file.
Next, review the users_controller.rb file and add the methods new & create. When new is called, instantiate a new blank User object (@user). Under the create method, we can modify a new user element leveraging the parameters submitted by the user (email, password, password_confirmation) to create the user.
Explanation of the Intended Flow -
- User clicks "signup" and is sent to /signup (GET request).
- User is routed to the "new" action within the "user" controller and then the HTML content is rendered from - /app/views/users/new.html.erb.
- Upon filling in the form data presented via new.html.erb, the user clicks "submit" and this data is sent off, this time in a POST request, to /users.
- The POST request to /users translates to the "create" action within the "user" controller.
Now, obviously we are missing something.....we need a signup page! Let's code that up under new.html.erb.
The internals of Rails and how we are able to treat @user as an enumerable object and create label tags and text field tags might be a little too complicated for this post. That being said, basically, the @user object (defined in the User controller under the new action - ex: @user = User.new) has properties associated with it such as email, password, and password confirmation. When Rails renders the view, it generates the parameter names based off the code in this file. In the end, the parameters will look like something like user[email] and user[password_confirmation], for example. Here is what the actual request looks like in Burp...
|Signup form generated by the code within /app/views/users/new.html.erb|
|Raw request of signup form submission captured.|
Okay, so, now we have registered a user. The last piece here is to have a home page to view after successful authentication and also code the logout link logic so that it actually does something.
In order to do this, let's make a quick change in the sessions controller. Under the create method, we change home_path to home_index_path as well as create a destroy method which calls the Rails method "reset_session" and redirects the user back to the root_url. Also, remove the content within the index action under the home controller.
Okay, here is what I mean...
|Session Controller - Note changes on Lines 9 and additions on Lines 16-19.|
|Home Controller - Note that the code contained within the index action has been removed.|
Note: If you see any security holes in some of the code shown in this series please remember that's kind of the point and refrain from commenting until later.