24 Oct

Ruby on Rails password validation - Rails Tricks Issue 21

This week, I want to cover a common compliance requirement. If you ever went through a PCI, ISO 2007, SOC2, or similar compliance questionnaire, you found the following question in one form or another:

Use sufficiently strong and robust authentication methods to protect authentication credentials from being forged, spoofed, leaked, guessed, or circumvented.

Let’s see what we need to do to satisfy this requirement.

First of all, you must have a strong password policy.
I recommend asking for a minimum of 12 characters, with at least one uppercase letter and one number.

You can use Active Record validations for this. If you have a password attribute on your model, you can add a validation similar to this example:
validate :password_complexity

def password_complexity
  if password.present? and !password.match(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d).{12,}$/)
    errors.add :password, "must include at least one lowercase letter, one uppercase letter, one digit, and needs to be minimum 12 characters."
Additionally, you should validate that the password is not leaked. Luckily, there is a gem for that: https://github.com/philnash/pwned.
After installing the gem, All you need to do is add the following validation to the model:
  validates :password, not_pwned: { on_error: :valid }
This will make a request to haveibeenpwned.com, mark the password as invalid if it has been pwned, and mark it as valid in case of a network or API error. You can find information about various configurations in the readme of the gem.

The final thing to prevent is credentials leaking. For this, you should store the passwords hashed with a robust hashing algorithm such as bcrypt.

That’s it for now. I will publish a longer post tomorrow on my blog about securing authentication in Rails apps.