Reset Password with Rails

The always brilliant Ryan Bates from Railscasts had a recent episode: #274 Remember Me & Reset Password

I have a slightly different way of handling password resets and thought I’d share.

Ryan walks you through adding two attributes to a User model: password_reset_token and password_reset_sent_at

I am not a fan of adding extra columns to my database tables, especially if only a few records will use it temporarily.

If you follow along with episode #250 Authentication From Scratch , you should have two columns: an encrypted password column password_hash, and the salt password_salt.

What I do differently is use ActiveSupport::MessageVerifier to create self-expiring, encrypted tokens for password resets.

I encrypt a date in the future using the user’s current hashed password as the salt.

I then append the encrypted date string to the end of the user’s id and encrypt that using the secret_token that Rails generates on app creation (in config/initializers/secret_token.rb).

In the User model, I add two methods to do this:

  def generate_token
    ActiveSupport::MessageVerifier.new(Rails.configuration.secret_token).generate([id, 1.day.from_now, password_digest])
  end

  def self.find_by_token(token)
    begin
      user_id, expiration = ActiveSupport::MessageVerifier.new(Rails.configuration.secret_token).verify(token)
      if expiration.future?
        Member.find(user_id)
      end
    rescue
      nil
    end
  end

Example:

the_user = User.first
reset_token = the_user.generate_token
# Mail reset_token to the_user

# … verify token and reset password:

if the_user = User.find_by_token(reset_token)
  # reset_token is valid, OK to change password
  the_user.password = new_password
end

Pros:

  • No extra, unused columns in your database tables.
  • An attacker needs two keys ( user’s password digest and your apps secret_token) to compromise a user account.
  • Once a password is changed on a user account, all tokens for that user are immediately invalid

Con:

  • If an attacker gains access to a copy of your users table and codebase you’ll have to regenerate your secret_token to prevent them from being able to reset *all* the user passwords

protect your servers with fail2ban

Fail2ban is server software that watches your log files for multiple failed login attempts, then uses iptables to temporarily ban the source ip address.

I’m surprised it took me this long to find this.

With apt-get, it’s as simple as:

 apt-get install fail2ban

The default configuration ( /etc/fail2ban/jail.conf ) is pretty good out of the box.

mysql memory tables and sessions

If you absolutely need to store session data in your database, don’t use memory tables to store session data, InnoDB works just fine.

If you need performance use something like memcached.

Memory tables are fast, but you’ll most likely run out of table space before enjoying any benefits.