Posted about 5 years ago. Visible to the public.

Fixing authentication in legacy applications

Authentication is hard: there are many edge cases, and most users (including yourself) usually only go the "happy path" once and never see the edge cases. If you have rolled your own authentication, or been using older authentication solutions, or resorted to HTTP Basic Authentication, this card will tell you what to do to make your application safe.

Any application that stores sensitive data in the browser

That is: cookies, e.g. by offering a login.

Issues with Clearance

Clearance may install routes we don't need, e.g. a sign_up_url for internal-only sites.
  • override each route in routes.rb: match 'sign_in' => redirect('/') (redirects to home page)
  • if you don't want any of the routes from clearance-0.8: simply remove Clearance::Routes.draw(map) from config/routes.rb

When you're done, check your changes by running rake routes.

When Rails gets a request with wrong/missing CSRF-Token, it calls ApplicationController#handle_unverified_request and continues processing the request!. Per default, the method only resets the Rails session, but since Clearance doesn't store its session there, you should delete the remember_token cookie.

With Clearance < 0.10.5, you need to do it yourself:

class ApplicationController < ActionController::Base # ... private def handle_unverified_request super sign_out end end

Issues with basic authentication

For basic authentication, the issue is similar as above. Resetting the session has no effect as the authentication data is not stored in the session. A simple counter-measure is to crash:

def handle_unverified_request super raise "Access Denied." end

Issues with home-made authentication

If possible, don't do this! It's really easy to get this wrong. Use a proven authentication library like Clearance or Devise instead.

If you really, absolutely, positively need to grow your own authentication solution, remember to consider the following:

Migrate unsalted user tables without changing existing passwords
Use salts to disarm brute force attacks.

In order to let users keep their passwords, hash passwords as before, then hash the result again with salt and only
store this to the DB.

  • add a column salt to the users table
  • initialize each record with a random string
  • update all stored passwords in a migration; see the attached migration (adjust model/column names)
  • implement this double-hashing in creation and authentication of users

You may want to use this method:

private def salted_hash(plain_password) # something like has_defaults would be nicer, but fails with e.g. :password => 'foo' self.salt ||= ActiveSupport::SecureRandom.hex(20) if new_record? # this double-hashing technique is explained in # Digest::SHA1.hexdigest(old_password_hashing_method(plain_password) + salt) end
Remembering user via cookie in an insecure fashion
The Rails session cookie is signed, which means you cannot modify it without Rails noticing. However, if you are using the cookies hash to store data a user must not tamper with (e.g. the ID of the logged-in user), sign it like this: cookies.signed[:user_id]. In addition, employ the SafeCookies middleware explained in this card. It will prevent attackers from reading your cookies by malicious Javascript and prevent the user's browser from sending it over insecure HTTP connections.

Generally: prefer tokens over ids for user identification, because they allow resetting sessions and more.

When using a session store other than Rails' CookieStore
Regenerate the session id when logging in. After authentication, but before writing to the session, call reset_session. (Know this also wipes all data from the session – which might be ok when logging in.) This prevents session fixation, where an attacker foists a session on you and thereby would be logged in when you are.
Growing Rails Applications in Practice
Check out our new e-book:
Learn to structure large Ruby on Rails codebases with the tools you already know and love.

Author of this card:

Dominik Schöler
Last edit:
3 days ago
by Dominik Schöler
About this deck:
We are makandra and do test-driven, agile Ruby on Rails software development.
License for source code
Posted by Dominik Schöler to makandra dev