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.
That is: cookies, e.g. by offering a login.
http
protocol into URLs that point to your application, which makes you vulnerable to
SSL-stripping
Show archive.org snapshot
.
//
(no protocol), which makes the browser use the protocol of the requesting pagehttp://
into mailer templates, where you need a protocol. See make ActionMailer use the correct protocol for links or have the current protcol as a configuration option of each environment.
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)
Clearance::Routes.draw(map)
from config/routes.rbWhen 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
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
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:
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.
salt
to the users tableYou 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.
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.