Class: PasswordIsStrongValidator
- Inherits:
-
ActiveModel::EachValidator
- Object
- ActiveModel::EachValidator
- PasswordIsStrongValidator
- Defined in:
- app/validators/password_is_strong_validator.rb
Overview
Validates that the password is strong.
Constant Summary collapse
- COMMON_PASSWORDS =
Password that are used too often and will be easily guessed.
%w{ password pass root admin metasploit msf 123456 qwerty abc123 letmein monkey link182 demo changeme test1234 rapid7 }
Instance Method Summary collapse
-
#contains_repetition?(password) ⇒ Boolean
private
Password repetition (quite basic) -- no "aaaaaa" or "ababab" or "abcabc" or "abcdabcd" (but note that the user can use "aaaaaab" or something).
-
#contains_username?(username, password) ⇒ false, true
private
Whether username is in password (case-insensitively).
-
#is_common_password?(password) ⇒ Boolean
private
Whether
password
is in COMMON_PASSWORDS or a simple variation of a password in COMMON_PASSWORDS. -
#is_simple?(password) ⇒ false, true
private
Returns whether the password is simple.
-
#validate_each(record, attribute, value) ⇒ void
Validates that
value
is a strong password.
Instance Method Details
#contains_repetition?(password) ⇒ Boolean (private)
Password repetition (quite basic) -- no "aaaaaa" or "ababab" or "abcabc" or "abcdabcd" (but note that the user can use "aaaaaab" or something).
53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 |
# File 'app/validators/password_is_strong_validator.rb', line 53 def contains_repetition?(password) if password.scan(/./).uniq.size < 2 return true end if (password.size % 2 == 0) and (password.scan(/../).uniq.size < 2) return true end if (password.size % 3 == 0) and (password.scan(/.../).uniq.size < 2) return true end if (password.size % 4 == 0) and (password.scan(/..../).uniq.size < 2) return true end false end |
#contains_username?(username, password) ⇒ false, true (private)
Whether username is in password (case-insensitively).
79 80 81 82 83 84 85 86 87 88 89 90 91 92 |
# File 'app/validators/password_is_strong_validator.rb', line 79 def contains_username?(username, password) contains = false unless password.blank? or username.blank? escaped_username = Regexp.escape(username) username_regexp = Regexp.new(escaped_username, Regexp::IGNORECASE) if username_regexp.match(password) contains = true end end contains end |
#is_common_password?(password) ⇒ Boolean (private)
Whether password
is in COMMON_PASSWORDS or a simple variation of a password in COMMON_PASSWORDS.
98 99 100 101 102 103 104 105 106 |
# File 'app/validators/password_is_strong_validator.rb', line 98 def is_common_password?(password) COMMON_PASSWORDS.each do |pw| common_pw = [pw, pw + "!", pw + "1", pw + "12", pw + "123", pw + "1234"] if common_pw.include?(password.downcase) return true end end false end |
#is_simple?(password) ⇒ false, true (private)
Returns whether the password is simple.
112 113 114 |
# File 'app/validators/password_is_strong_validator.rb', line 112 def is_simple?(password) not (password =~ /[A-Za-z]/ and password =~ /[0-9]/ and password =~ /[\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x3a\x3b\x3c\x3d\x3e\x3f\x5b\x5c\x5d\x5e\x5f\x60\x7b\x7c\x7d\x7e]/) end |
#validate_each(record, attribute, value) ⇒ void
This method returns an undefined value.
Validates that value
is a strong password. A password is strong if it meets the following rules:
- SHOULD contain at least one letter.
- SHOULD contain at least one digit.
- SHOULD contain at least one special character.
- SHOULD NOT contain
record.username
(case-insensitive). - SHOULD NOT be in COMMON_PASSWORDS.
- SHOULD NOT repetitions.
26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
# File 'app/validators/password_is_strong_validator.rb', line 26 def validate_each(record, attribute, value) return if value.blank? if is_simple?(value) record.errors[attribute] << 'must contain letters, numbers, and at least one special character' end if contains_username?(record.username, value) record.errors[attribute] << 'must not contain the username' end if is_common_password?(value) record.errors[attribute] << 'must not be a common password' end if contains_repetition?(value) record.errors[attribute] << 'must not be a predictable sequence of characters' end end |