I need to implement password security with various flexible requirements. The requirements are essentially taken from the Sans password policy:
Strong passwords have the following characteristics:
Contain at least three of the five following character classes:
- Lower case characters
- Upper case characters
- Numbers
- Punctuation
- “Special” characters (e.g. @#$%^&*()_+|~-=`{}[]:";'<>/ etc)
- Contain at least fifteen alphanumeric characters.
There are also requirements of not allowing the users email address to appear in the password.
Ideally, I would like a gem that would handle this - it would be more widely tested and used, and we would be less likely to have bugs.
If no gem covers this, then what is the best and most-hardened method to handle these requirements? Ideally we would be able to say that we are not only safe, but safe because we have an industry standard implementation.
The gems I have found so far are:
and
(We are using Rails 3.2.17 and Ruby 1.9.3, but are planning on moving to Rails 4 and Ruby 2 soon, so newer solutions are also welcome).
we've been using devise security extension for a while now. It has a lot of interesting features (e.g. password history, password expiration...).
For password complexity we wanted something that was a bit more configurable (in your example: letting customer choose how many levels of comlexity they wanted).
So we rolled out our own solution based on a score: 3 out of 5 (in you example) means that we're testing each characterisitc and give one point if it's found. If the total score is equal or greater than required score, then the password is fine.
In terms of code, our validation looks like this:
validate :password_complexity
def password_complexity
return if password.nil?
if password.size < 8
errors.add :password, "Must be at least 8 characters long."
return
end
required_complexity = 3 # we're actually storing this in the configuration of each customer
if !CheckPasswordComplexityService.new(password, required_complexity).valid?
errors.add :password, "Your password does not match the security requirements."
end
end
and the service that checks for the complexity looks like this:
class CheckPasswordComplexityService
attr_reader :password, :required_complexity
def initialize(password, required_complexity)
@password = password
@required_complexity = required_complexity
end
def valid?
score = has_uppercase_letters? + has_digits? + has_extra_chars? + has_downcase_letters?
score >= required_complexity
end
private
def has_uppercase_letters?
password.match(/[A-Z]/) ? 1 : 0
end
def has_digits?
password.match(/\d/) ? 1 : 0
end
def has_extra_chars?
password.match(/\W/) ? 1 : 0
end
def has_downcase_letters?
password.match(/[a-z]{1}/) ? 1 : 0
end
end
Then it becomes very easy to add some new characteristics you want to check.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With