How to Set Password Requirements in Rails

A peek into more complex validations and basic password security for those new to Rails

So hopefully you’ve been working in Rails for at least a few weeks and have been validating your models up till now. The presence of a username, the presence of an email, maybe even some numericality for item inventories, and who knows what else! Now you’re looking to create all those pesky rules for passwords when someone signs up for your Rails app? Good! Users are lazy and the easier they can make their passwords to remember (hack) they will.

Password Hashing

Before we even get to password requirements, make sure you are never storing user passwords as plaintext. Heaven forbid someone gets a look at your database they will be able to screenshot all of the users passwords including any admins that might be present on the system. Blasphemy and chaos!

To deal with this many developers hash the plain text password input and store the hash value in their database instead. Hashing is a coding process similar to a chemical reaction, irreversible. Making it very hard to reverse engineer the hash value back to the original password. This helps protect you and your users even if someone you don’t trust gets a peek at your database.

To implement this technique I suggest using bcrypt. It’s a rails gem that allows you to hash a value using SHA256 which takes in any string input and spits out a new 32 byte string for you to store in your database. Example:

First lets add or uncomment out (in Rails it’s usually there unless you deleted it) …

… in your Gemfile within the main section (not development or test) and run your bundler to install this handy tool. This will install it within your app for use and will be included in your packaged app if/when you’re ready for production.

Now all you need to do is add a line to your model file and change your password column in your CreateUser migration file to be password_digest. Only do this if your project isn’t in production, if it is create a new migration to change the column name in your User table instead.

Add this line before the laster end of your User model file.
From this…
To this!

Drop, create and migrate your database to put these new changes in effect (again if in production DO NOT DROP database). The hashed password value will be stored instead of your plain text passwords. This way you never store the plain text passwords for anyone to find.

Cool! Now hackers will have a very hard time trying to figure out our users’ passwords even if they find them. Now how about brute force attacks? Trying all the combinations of characters, numbers, and symbols that a password might be? Well there are some techniques out there to increase the time between attempts and eventually lock the account after a certain number of attempts. Both great ideas that I suggest you look into. However, that is a fair bit more complicated than what I want to get into today.

Regular Expression Constant

Instead, today, let’s force our lazy users to create more complicated passwords that will be harder to brute force hack. To get started you need nothing more than Rails, easy! Presumably you have a User model that houses validations, relationships and relevant methods for your user table. Let’s get started!

Generally, a good base would make sure all passwords are at least a certain length (let’s say 8), contain at least one uppercase and one lowercase letter, a number and a symbol. With these restrictions the number of possible passwords is in the trillions if not quadrillions (I tried to find the exact number but all I found were mathematicians bickering).

Awesome, but how do we check if the password a user passes in fits all those criteria? Validations and Ruby regular expressions(regexp), yay! Rails has an awesome built in validation, format, that takes in a regexp as a value and compares what we’re validating to it. Let’s make the regexp a constant to pass into the format validation.

“What does that even mean?!” you say? Well /\A denotes the start of the string we are examining and the /x denotes the end of it. The /x also allows us to put whitespace between our different regexp tokens we will be adding in between the /\A and /x. Now that we’ve got the setup done, let’s start adding the regexp tokens we need for our password requirements. I’m going to start with the minimum length of 8 characters.

Awesome, more gobbly gook! Let’s break down this new line we’ve added.

  • ‘?=’ tells our program to look from the start of the string at what it contains.
  • The ‘.’ tells our regexp token that there should be no characters before what we pass the regexp token next.
  • Finally the ‘{8,}’ tells us that we are expecting the string to have a minimum length of 8 but no maximum length.

That’s right super long passwords are in! All together we are expecting our string to start with nothing before having at least 8 characters in length with no maximum. And this token doesn’t capture or change our original string in any way so we can keep checking other things and store the password as input, perfect!

Same as before with some new bits!

  • The .* combination denotes that the number could be the first character in the string (.) or have some other characters come before it (*).
  • The \d denotes we are looking for a number or digit.

This is the same as the digit line but instead we are making sure there is at least one lowercase letter from the range a-z.

This new line makes sure there is one uppercase letter from the range A-Z in the string.

This last line we are adding, simply put, checks to make sure there is one symbol in the string from the list of keyboard symbols.

Password Validation

Great! We’ve got an awesome regexp Constant to validate our password with. Now let’s implement it in our validations.

Now our validation will use our regexp constant to check our password for our requirements. Mission complete!

PS

Make sure to run your tests because if there was any user creation or updating they most likely have been broken. To fix, change any passwords you use in your tests to pass all your password requirements. Also make sure to sad path test what happens when someone doesn’t fulfill all the password requirements!

Resources: