Hashing Out with BCrypt
One Way Hashing
A hashing algorithm, or a digital fingerprint, is a one-way conversion, meaning that once the hash is run on an input — there isn’t a way to get back to the original input, which makes a great option for storing passwords. But, they aren’t impossible to crack; 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 — also known as a rainbow table.
A resolution to this is a salt, which is a random value used to increase security to the beginning of a password before it is hashed. Now, if two people both have the same password, their salts will be different — thus making their hashes different. The salt and the hashed password are stored within the database, so that you are able to determine the salt part of the hashed password when checking potential ones.
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) validations, salting, hashing and authenticating passwords.
You can use BCrypt in Ruby with the `bcrypt` gem. If you created a new Rails app through rails new project-name
, BCrypt should exist in your gem file — however it will be commented out. Anyways, make sure that gem ‘bcrypt' , ‘~> 3.1.7’
exists within your gem file, then run bundle install
.
Now that that’s taken care of, let’s create some new passwords in the rails console:
BCrypt is a module that has a Password class within it, which is why we’re calling methods on BCrypt::Password. If you look, you can see we’ve essentially created the same password all three times, but got a different hash back each time. This is because the create method is taking the input, generating a salt, and then hashing them together. If you were to save the new password to a variable, you can then see the hashed salt using the salt method.
This is super important for authentication because we’re going to need to be able to check a plaintext password against the hashed password, while excluding the salt.
Sweet, let’s authenticate a plaintext password against one of the hashes:
All right! So let’s go over what’s happening here:
- The new method normally creates a new instance, but it doesn’t save it to the database. BCrypt::Password is overwriting the new method for the purpose of checking potential passwords. Notably, BCrypt::Password.new(hashed_pass) initializes a new BCrypt::Password instance with the data from hashed_pass, where hashed_pass exists in the database as a BCrypt::Password instance.
- ‘password’ and ‘test012’ are just plaintext passwords we want to test against the hashed password.
- == is a comparison operator that we use to check the equality of operands. BCrypt::Password, which takes from the String class, overwrites the == method to check potential plaintext passwords against the hashed password. Essentially, 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 app
Rails versions above 3, comes shipped with ActiveModel::SecurePassword
, which uses bcrypt-ruby. All you have to do, to use this is:
- Include gem ‘bcrypt’, ‘~> 3.1.7’ in the gemfile, and please don’t forget to run bundle.
- Then, you’re gonna create a User model with a password_digest attribute that’s a string.
- Lastly, you’re going to include has_secure_password in the User model:
class User < ActiveRecord::Base
has_secure_password
end
Here, has_secure_password, is essentially:
- Adding password and password_confirmation attributes to the User model.
- Adding validations with creation for presence of password, the length of password less than or equal to 72 bytes, and confirmation of password (which uses the password_confirmation attribute to do so). It should be noted that password confirmation is completely optional, and if the value of the attribute is nil, the validation will never be triggered.
- Adding the methods to create and authenticate a BCrypt password, that would look similar to this if we had to write it alone:
class User < ActiveRecord::Base
def password
@password
end def password=(plaintext_pass)
self.password_digest = BCrypt::Password.create(plaintext_pass)
end def authenticate(plaintext_pass)
if BCrypt::Password.new(self.password_digest) == plaintext_pass
true
end
end
end
This is getting long, so without going on and on about authentication setup, the next steps that I would include would be something along the lines of:
- Creating a login and signup views with forms using the
:password
and maybe:password_confirmation
attributes. - Then making a signup controller, that would create a new user and use the
password=
method (as shown above), to hash the password that was input into the form. - Lastly, making a login controller, which would use the
authenticate
method to check if the plain text password that was entered into the form matches the hashed password in the database for that specific user.