Persisting passwords in plain text does not seem to be a very good idea. If for any reason an attacker manages to get access to your database they would be able to see all your users’ passwords without any restriction, giving them the possibility to authenticate as your users… and as you know, despite of constantly advising not to do so, still many people use the same password in many sites… so it can even get worse… (I hope it is not your case 😉). Fortunately Rails gives you a way to easily store passwords in an irreversible encrypted way, as well as auxiliary authentication and validation methods.

Adding has_secure_password

Let us see what you need to do to use has_secure_password in your Rails application.

The macro has_secure_password adds methods to set and authenticate against a BCrypt password. This indicates that you need to include a library that provides BCrypt logic to your Rails application. Since this is a common use case, the default Gemfile generated by your Rails application already includes the bcrypt gem. The only thing you need to do is uncomment that line, as you can see in the following image, and then run bundle install.

Uncomment bcrypt.

Now run bundle install to get this dependency installed into your Rails app.

has_secure_password is defined in ActiveModel::SecurePassword. If you are using has_secure_password with Active Record, as I will be doing in the examples bellow, you do not need to do anything extra since Active Record automatically includes ActiveModel::SecurePassword.

To see has_secure_password in action, create a User model with some fields, in this example just a unique email, and a password_digest field. It is important that the field in which the password is going to be persisted ends with _digest. In this example, it will be the default password_digest.

Let’s take advantage of Rails’ scaffolding to create this User model. In a terminal, execute the following:

Once the scaffold generator has created all the needed files, it is time to migrate the database. In a terminal, run the following command:

The resulting database schema should look similar to the one in the following listing:

As you can see, Rails has added a password_digest field, which as we mentioned before, has the _digest suffix, necessary for has_secure_password to work.

If the field that is going to be used to persist the passwords is called password_digest, then in the User model you just need to add the has_secure_password macro with no further indications.

If instead you decided to name it for example recovery_password_digest, then in the User model you need to indicate the name of the field, without the _digest suffix. Like this:

If you start the Rails server by running bin/rails server on a terminal, and you navigate to the user creation page, you should see something similar to the following image.

Create User form with has_secure_password

Since sign up and authentication forms tend to be different in each Rails application we will now focus on the methods and validations added by has_secure_password. To do that we will open a Rails console and run some commands there. To start the Rails console, in a terminal, run bin/rails c, like this:

Now let’s create a user, first without providing a password, so that has_secure_password validations kick in telling you that you need to provide one, and then providing a password:

Let’s now provide a password so that the user can be persisted:

If you pay attention to that transaction log, you will notice that Rails is not persisting anything in a password field, but instead in password_digest, whose value appears already FILTERED in the logs for security reasons without having to do anything about it. AWESOME 🙌.

If you wanted to see what is in there, you can just fetch the user’s password_digest:

As you can see, it has been encrypted so that it does not have anything to do with the original value. This encryption is irreversible, which means, that the original value cannot be decrypted from the value of password_digest.

In order to authenticate the user, has_secure_password provides the authenticate method, which accepts a password string as a parameter, encrypts it using the same algorithm as the one used to persist password_digest and then compares the result with the value stored at password_digest. If they match, the result will be true, otherwise false.

In a typical Rails application’s SessionsController, you would first fetch the user by email and then check using the authenticate method like this:

There is an extra validation that it is ignored by Rails unless you provide a value for it: password_confirmation. When a password_confirmation is provided, both password and password_confirmation values must match so that the user can be persisted:

If you just want to ignore all these validations you can just provide the option validations: false to your has_secure_password macro in the User model. Like this:

And that is basically it! Now that you know how to secure passwords, in the next post we will take a look the recently added by Rails 7 Active Record Encryption feature, and the differences it has with has_secure_password.

If you enjoyed this post, do not forget to subscribe so that you do not miss any of my future updates. If you want me to write about something in particular, you can let me know about it in the comment section below.

See you in my next post! Adiós!

One Response