Read more

Fixing authentication in legacy applications

Dominik Schöler
April 26, 2013Software engineer at makandra GmbH

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

Illustration web development

Do you need DevOps-experts?

Your development team has a full backlog? No time for infrastructure architecture? Our DevOps team is ready to support you!

  • We build reliable cloud solutions with Infrastructure as code
  • We are experts in security, Linux and databases
  • We support your dev team to perform
Read more Show archive.org snapshot

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

Issues with Clearance

Clearance Show archive.org snapshot 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_up' => 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.

handle_unverified_request

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. User.new :password => 'foo'
    self.salt ||= ActiveSupport::SecureRandom.hex(20) if new_record?
  
    # this double-hashing technique is explained in
    # https://makandracards.com/makandra/15827-checklist-for-implementing-authentication
    Digest::SHA1.hexdigest(old_password_hashing_method(plain_password) + salt)
  end

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.

Dominik Schöler
April 26, 2013Software engineer at makandra GmbH
Posted by Dominik Schöler to makandra dev (2013-04-26 11:57)