Bcrypt Gem and Password Hashing

Justin Tollison
Programmer’s Journey
4 min readApr 9, 2022

Another month and another thing learned, today we’ll be going over the Bcrypt gem in Ruby, specifically Ruby on Rails and about what it provides to your code! First of all, what are Gems? Gems in Rails are code libraries that allow any developer to add functionalities to their code without having to write it themselves. Essentially, it’s for adding features to your project and saving you a lot of hassle. So, let’s start with installing Bcrypt to out project. Here is also the github repo to bcrypt: https://github.com/bcrypt-ruby/bcrypt-ruby

gem install bcrypt

Now that we have Bcrypt installed, we probably should answer the question, “why should I use bcrpyt(),” or “what is bcrypt() for?” Let’s say you’re building a project that uses the MVC architecture, (model-viewer-controller) and interacts with a database in order to retrieve and display the information on a webpage. Well, if we have users on the platform, they’ll require passwords in order to login. Now, it’s always recommended to have different passwords for different websites, but people sometimes use the same password and if a hacker was able to access your database, they could retrieve the user’s password. Emails are normally common knowledge, so the hacker could try to use the same password on your website as the user uses on their email and could hack into it. Not to mention that the hacker could also mess around with the person’s profile, steal data and such other malicious actions. Thankfully, Bcrypt allows us to combat this and other attacks from hackers by using a hashing function.

Photo by Bermix Studio on Unsplash

bcrypt is a password-hashing function designed by Niels Provos and David Mazières, based on the Blowfish cipher and presented at USENIX in 1999.[1] Besides incorporating a salt to protect against rainbow table attacks, bcrypt is an adaptive function: over time, the iteration count can be increased to make it slower, so it remains resistant to brute-force search attacks even with increasing computation power.”

To summarize what password-hashing does, it takes a plaintext string that the user has entered using a one-way mathematical function and stores it. The value comes from a hash function that cannot be reversed to reveal the plain text. There is a much more complicated answer, such as using cryptographic keys, but this answer will suffice for now. Essentially, bcrypt takes what you have entered, runs it through a function and returns a secret value that cannot be encrypted. Encryption is two-way, while hashing is one-way. Bcrypt also incorporates a salt for protection against rainbow tables, which is to say that even after the value is hashed, it enters completely randomized values at the end of the hash. A rainbow table is if multiple users use the same password and the hacker gets a table of the same hash values, Salts guarantee that no two hash values will be the same.

So, we have a database with users and user passwords on our Ruby on Rails project and now we have bcrypt installed. We’re on our way into protecting our users valuable most likely multi-used password.

ActiveRecord comes with a macro has_secure_password which adds methods that allow you to authenticate a password against a Bcrpyt password. has_secure_password also generates a password_digest after the Bcrypt gem has hashed the user’s password. The authenticate method from has_secure_password will match the plain text a user has entered(and hashes it) to the hash value stored in password_digest. We’re not storing the actual password, we’re just holding the digest of the password. Phew, that’s a mouthful.

Continuing, how do we actually use has_secure_password along with Bcrypt? Well, we’re almost there! Let’s start with creating out users table along with the password_digest in it.

class CreateUsers < ActiveRecord::Migration[6.1]
def change
create_table "users" do |t|
t.string :username
t.string :email
t.string :bio
t.string :password_digest
end
end
end

Once you get that migrated and created, we should also create our User model file, let’s go into that and add the has_secure_password macro in order to give us access to the authenticate method.

class User < ApplicationRecord
has_secure_password
validates :username, presence: true
validates :username, uniqueness: true
end

We also added some validations for when creating your username. As another note, has_secure_password provides us with all the password validations already built into password_digest so that we don’t need to add validation code for our user’s passwords! Aren’t macros great? Next let’s look at creating a user in our user’s controller.

def create       
user = User.new(user_params)
if user.save
session[:user_id] = user.id
render json: user, status: :created
else
render json:
{ errors: user.errors.full_messages },
status :unprocessable_entity
end
end

Here we’re creating a new user model with user_params that are defined in a private method that’s not shown and if the new model passes validations and is saved onto our database, we set the current user_id’s sessions to the newly created user.id. Once the user is signed up, we can log them in using a sessions controller.

def create        
user = User.find_by(username: params[:username])
if user&.authenticate(params[:password])
session[:user_id] = user.id
render json: user
else
render json: { errors:
["Invalid username or password"] }, status: :unauthorized
end
end

And finally here’s our method in our sessions controller for logging the user in! As you can see, we add the authenticate method and passing in the :password params into it to authenticate the plain text that the user has entered to the password_digest. This sets the current user_id of the session to the user.id of the User that’s found with using the find_by method.

That ended up being a lot longer than I wanted, but I hope it was somewhat thorough on what bcrypt is and how to use it along with has_secure_password! I am by no means an expert and I recommend going over the ActiveRecord documentation as well as the Bcrypt documentation! Happy coding!

--

--

Justin Tollison
Programmer’s Journey

Unity Game Developer and Flatiron Software Engineering Alumni