TechHui

Hawaiʻi's Technology and New Media Community

Two-Factor Authentication in your Rails App with Devise & Yubikey

Most rails applications have some sort of "user" to represent either customers who are consuming the service or administrators that are publishing content to the web application.  It's important that these users are authenticated (ensuring that they are who they say they are).  For user authentication in a Ruby on Rails application, Devise is one of the best solutions out there.  It has a very active community and wide variety of options and extensions to fit your business model.  Sometimes customers may ask for or the application requires an additional layer of authentication beyond the basic username/password combination.  

Enter Yubikey.  

Yubikey Hardware is a small usb "key" that is set up to generate unique one-time passwords that are validated against their server.  Fortunately someone has written an extension to incorporate the yubikey as a second factor of authentication against devise users called yubikey_database_authenticatable.  

In order to require users to log in with a yubikey there are a few large steps you have to take.

  1. Have a rails application
  2. Set up regular 'database_authenticatable' user authentication with devise. And require user authentication for access to all or part of the web application.
  3. Add 'yubikey_database_authenticatable' gem to your project and set up the user model and table to use yubikey authentication.
  4. Generate the devise views and customize the session login page to include the yubikey one-time password.
  5. Add user managment/administration to the application for regulating and associating users with a yubikey.

I have created a skeleton rails app with these steps and posted it to Github

 

1. Have a rails application

For this app I am using rails 3.2.10


$ rails new rails_with_yubikey
$ cd rails_with_yubikey/

At Ikayzo we have a fondness for lemurs so this application will maintain a list of lemurs species.


$rails g scaffold lemurs species:string description:text

Now that we have some actual pages in the applications we can set the root url of the application to the LemursController


$ rm public/index.html
# config/routes.rb
...
root :to => 'lemurs#index'
...

2. Set up regular devise authentication

There is already some great documentation for this on the Devise readme but here are the steps I took to add it to this rails application.

Add devise to the global part of the Gemfile


# Gemfile
...
gem 'devise'
...

$ bundle install

Next generate the devise files and user model


$ rails generate devise:install
$ rails generate devise User
$ rake db:migrate

The only thing left is to require users to log in when accessing the lemursController


# app/controllers/lemurs_controller.rb
class LemursController < ApplicationController
before_filter :authenticate_user!
...
end

 

3.  Add yubikey_database_authenticatable extension to devise

First add the yubikey_database_authenticatable gem to the applications Gemfile


# Gemfile
...
gem 'devise'
gem 'yubikey_database_authenticatable'
...
$ bundle install

There are two new columns that the users table will need to authenticat witht he yubikey.


$ rails generate migration add_yubikey_to_users

# db/migrate/TIMESTAMP_addyubikey_to_users.rb
class AddYubikeyToUsers < ActiveRecord::Migration
def change
add_column :users, :useyubikey, :boolean
add_column :users, :registeredyubikey, :string


end
end

$ rake db:migrate

 

You will have to replace :database_authenticatable with  :yubikey_database_authenticatable to the devise line of the user model.

# app/models/user.rb
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :token_authenticatable, :encryptable, :confirmable, :lockable, :timeoutable and :omniauthable
devise :yubikey_database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable, :timeoutable
# Setup accessible (or protected) attributes for your model
attr_accessible :email, :password, :password_confirmation, :remember_me,
:useyubikey, :registeredyubikey, :yubiotp
attr_accessor :yubiotp
def registeredyubikey=(yubiotp)
write_attribute(:registeredyubikey, yubiotp[0..11])
end
end

 

There is one last change to make to the code of the project.  The field for the yubikey one-time password is not in the default user session login screen for devise.  You will need to generate the devise views and overwrite the new user session page.

 

$ rails generate devise:views
# app/views/devise/sessions/new.html.erb
<h2>Sign in</h2>
<%= form_for(resource, :as => resource_name, :url => session_path(resource_name)) do |f| %>
<div><%= f.label :email %><br />
<%= f.email_field :email %></div>
<div><%= f.label :password %><br />
<%= f.password_field :password %></div>
<% if devise_mapping.rememberable? -%>
<div><%= f.check_box :remember_me %> <%= f.label :remember_me %></div>
<% end -%>
<% if devise_mapping.yubikey_database_authenticatable? -%>
<div><%= f.label :yubiotp, "Yubikey One Time Password" %><br />
<%= f.password_field :yubiotp%></div>
<% end -%>
<div><%= f.submit "Sign in" %></div>
<% end %>
<%= render :partial => "devise/shared/links" %>

 

5. Add user managment to the application for regulating and associating yubikeys with users

For this application I just created a user and added a yubikey to them through the rails console.  To require a user to login with yubikey the boolean use_yubikey needs to be set to true. The other user also needs to have the registeredyubikey field set to their yubikeky.  I did this through the console by copying the text output from the yubikey to a clipboard and setting the field in the console.  Part of the code that was added to the user model is designed to peel of the first 11 characters of the yubikey one-time password which is its static and unique identifier.

In a real world application there will need to be features built in to managing the yubikey for the user.  If the customers/users will be providing their own yubikey they will need to update these settings themselves.  However if the yubikeys are distributed to a small group of users, managing these attributes should probably be hidden behind some administration side of the application.

Views: 1837

Comment

You need to be a member of TechHui to add comments!

Join TechHui

Comment by Christopher Kobayashi on December 12, 2013 at 9:03pm

Interesting article: Google Wants To Make Your Passwords Obsolete

Although U2F logins are not yet available to the public, Google has already deployed several hundred thousand YubiKey Neo devices to its employees since the beginning of 2013, according to Yubico CEO, Stina Ehrensvärd. Google’s Product Management Director for Information Security, Sam Srinivas confirmed the scope of the internal pilot program – as well as a 2014 public release – and says that the response to the device has been overwhelmingly positive, with employees remarking on the ease of use.

Comment by Brian on January 20, 2013 at 5:31pm
Yubikey's also vulnerable to realtime phishing attacks. You can simply use SMS authentication (probably easier because most already have phones, and defeats certain attacks that yubikey can't) and get similar results.

You can mitigate this by having the server authenticate to user.
Comment by Boris Ning on January 9, 2013 at 10:13pm

Hello, I noticed you're using the 3.2.10 version in your app.

There's a serious vulnerability that was released earlier two days ago (https://groups.google.com/forum/#!topic/rubyonrails-security/61bkgvnSGTQ/discussion) and the certain security group has already automated the exploitation scheme for it.

Metasploit article:

https://community.rapid7.com/community/metasploit/blog/2013/01/09/s...

Github code for exploitation:

https://github.com/rapid7/metasploit-framework/pull/1281

Please update your rail version.

Comment by Chris Sass on January 8, 2013 at 8:45am

Thanks John, I'm glad you liked the post!

It sounds like you fall into the first category of application I mentioned.  Since it's not feasible to distribute keys to all your users you would have to introduce this as an optional feature. Your customers would have to purchase their own yubikey and you would have to allow them to turn on the useyubikey boolean and set their registeredyubikey to the yubikey they purchased somewhere in your application.

I'm not sure how your business model works but the nice thing about it being an optional feature is that you can use it as an incentive to purchase/subscribe to the next tier.  This is what lastpass.com does.  

One other cost associated with introducing this is that you do have to build in features or support users changing/losing their keys.

Hope that helps

Comment by John on January 8, 2013 at 2:10am

Chris, that was a very informative post. Thanks!

I'd love to use two form authentication in our Rails app so this definitely caught my interest and I had never seen Yubikey before.

My main question is: How do I get my users to get a Yubikey? Does this make more sense for internal applications where users are all from the same company.

We have thousands of users / members from all over the world so I am just not sure if it could logistically make it work, though it would be great!

Sponsors

web design, web development, localization

© 2014   Created by Daniel Leuck.

Badges  |  Report an Issue  |  Terms of Service