The Magic Behind BCrypt

Amy Resnik
5 min readSep 12, 2019

We learned about implementing auth in a Ruby on Rails application earlier this week at Flatiron School. As a recap, auth has two major parts: authentication and authorization. Authentication is checking that you are who you say you are, and authorization is checking if you are allowed to do something.

Authentication in a web application often consists of validating usernames and passwords. We know that we shouldn’t store plaintext passwords in the database, so how can we conceal the password when a new user is created and still be able to check possible plaintext passwords against the concealed password when a user logs in?

If you store user passwords in the clear, then an attacker who steals a copy of your database has a giant list of emails and passwords. Some of your users will only have one password — for their email account, for their banking account, for your application. A simple hack could escalate into massive identity theft. https://github.com/codahale/bcrypt-ruby

Hashing Algorithms

A hashing algorithm is a one-way transformation, meaning once the hashing algorithm is run on an input there is no way to get back the original input from the hash, which makes a great option for storing passwords. However, they are not uncrackable; hackers can create a database of hashed passwords from the same algorithm and then look up passwords by their hash, since the same password will generate the same hash in the algorithm. This is called a rainbow table.

A solution to this is called a salt, which consists of adding a string of random data based on the type of computer, version of OS, and the time of day, plus some other factors to the beginning of a password before it is hashed. So now if two people both have their password as password their salts will be different, making their hashes different. The salt and the hashed password are then stored in the database, so that we are able to determine the salt portion of the hashed password when checking potential passwords.

BCrypt

BCrypt is a hashing algorithm used to securely store passwords and was designed by Niels Provos and David Mazières of the OpenBSD Project. BCrypt handles setting, (some) validating, salting, hashing, and authenticating passwords for us.

We can use BCrypt in Ruby via the bcrypt gem. If you created a new Rails application through rails new project-name, BCrypt should exist in your gem file but be commented out. Either way, make sure gem 'bcrypt', '~> 3.1.7 exists in your gem file, and don’t forget to bundle install.

Now let’s create some new passwords in rails console:

BCrypt::Password.create("password")
# => "$2a$12$W6ZLoEROAUu4JY/LeAuS9.r48bwAACnOAT5Uhc1geedlCU7eeLPAK"
BCrypt::Password.create("password")
# => "$2a$12$hUa5GTL71vGNszVRLAml5e5uvuP5vhd39in80KUP7u1rWxzGe3zDO"
BCrypt::Password.create("password")
# => "$2a$12$9CAunaxGAoskeFmOkDFUbeyYGiaNs9/kdj.qOUHe5jdzMFYFiS5L."

BCrypt is a module that has a Password class within it, which is why we are calling methods on BCrypt::Password. Notice how we created the same password three times and got a different hash back each time? This is because the create method is taking our input, generating a salt, and hashing them together. If we save our newly created password to a variable we can actually see the hashed salt through the salt method:

pw = BCrypt::Password.create("password")
# => "$2a$12$9CAunaxGAoskeFmOkDFUbeyYGiaNs9/kdj.qOUHe5jdzMFYFiS5L."
pw.salt
# => "$2a$12$9CAunaxGAoskeFmOkDFUbe"

This is important for authentication since we need to be able to check a plaintext password against the hashed password excluding the salt. Now let’s authenticate a plaintext password against one of our hashes:

BCrypt::Password.new("$2a$12$9CAunaxGAoskeFmOkDFUbeyYGiaNs9/kdj.qOUHe5jdzMFYFiS5L.") == "password"
# => true
BCrypt::Password.new("$2a$12$9CAunaxGAoskeFmOkDFUbeyYGiaNs9/kdj.qOUHe5jdzMFYFiS5L.") == "test123"
# => false

Let’s break down how this is working:

  1. The new method normally creates a new instance, but does not save it to the database. BCrypt::Password is overwriting the new method for the purpose of checking potential passwords. Specifically, BCrypt::Password.new(hash_pass) initializes a new BCrypt::Password instance with the data from hash_pass, where hash_pass exists in the database as a BCrypt::Password instance.
  2. “password” and “test123” are plaintext passwords we want to test against the hashed password.
  3. == is a comparison operator that we use to check equality of operands. BCrypt::Password, which inherits from the String class, is overwriting the == method to check potential plaintext passwords against the hashed password. Specifically, the == method is taking the salt from the BCrypt::Password instance (from the salt method), hashing the plaintext password with the salt, and then comparing that hash to the BCrypt::Password instance.

BCrypt in Rails: has_secure_password

Rails (above version 3) comes with ActiveModel::SecurePassword, which uses bcrypt-ruby. Once again, Rails is making our lives easy and all we have to do to use BCrypt is:

  1. Include gem 'bcrypt', '~> 3.1.7 in our gem file (don’t forget to bundle install)
  2. Create a User model with a password_digest attribute that is a string (and whatever you want your username attribute to be called)
  3. Include has_secure_password in our User model:
class User < ActiveRecord::Base
has_secure_password
end

So what exactly is has_secure_password doing?

  1. Adds password and password_confirmation attributes to the User model. Note: these should be used in forms as attributes, but are not DB columns, password_digest should be the only password column in the DB.
  2. Adds validations on creation for presence of password, length of password less than or equal to 72 bytes, and confirmation of password (using password_confirmation attribute). Note: password confirmation is optional and the validation won’t be triggered if the value of the attribute is nil.
  3. Adds methods to create and authenticate a BCrypt password, which would look something like this if we had to write them ourselves (see actual code here):
class User < ActiveRecord::Base
def password
@password
end
def password=(plaintxt_pass)
self.password_digest = BCrypt::Password.create(plaintxt_pass)
end
def authenticate(plaintxt_pass)
if BCrypt::Password.new(self.password_digest) == plaintxt_pass
true
end
end
end

Without fully getting into the rest of the authentication setup, next steps related to everything we talked about include:

  1. Creating login and signup views with forms using the :password and possibly :password_confirmation attributes.
  2. Making a signup controller action, which will create a new user and use the password= method to hash the password inputted into the form.
  3. Making a login controller action, which will use the authenticate method to check if the plaintext password inputted into the form matches the hashed password in the database for that user.

Sources:

--

--