- Read Tutorial
- Watch Guide Video
Great job on going through that section of the course. You should know how a full set of authentication features built right into your app such as registrations logins and even working directly with the user object as they navigate on the site. That's a fantastic job.
In this deep dive, I want to pick out one specific part of the authentication implementation and dissect that a little. I want to talk about the bcrypt gem that devise incorporates. It's hidden because it's a dependency that devise has and it seems to work kind of by magic.
- It encrypts our passwords
- It adds a custom salt, which means that the password encryption algorithm for our site would be completely different than someone else's site.
I want to take a sidestep and take a look at what it does and also how we could use it independent of devise if we ever needed to encrypt something ourselves.
In this deep dive I have two different windows open for two very cool gems that we going to work with. The first one is bcrypt and this is a hashing algorithm that can generate some great passwords with minimal effort. You can see in the summary that it says the bcrypt is a ruby binding for the open BSD bcrypt password hashing algorithm. That is a huge mouthful, and honestly don't worry if that sounds a little bit cryptic or a little bit over the top. Essentially what it's saying is that this is a library that can take in data such as passwords and then output a secure encrypted value. That's all it is and that's what we're going to use it for.
The next thing that I have open here is pry. We are getting get into pry in our debugging section, however, I want to give you a little intro right here so that you can get a feel for how it works and see the output and some things like that inside of the terminal. We're going to be implementing both of these items.
Let's implement pry. You are simply going to install it come to your terminal and paste in
gem install pry. This is going to install this for you. I'm going to come back here and we're going to do the same thing
gem install bcrypt Once we have both of these in place we're going to be able to run the code.
Pry is an alternative to using irb, we can simply type irb and we can start typing ruby code in. Irb is a little bit limited, whenever you work with professional developers and they're performing debugging or they're trying to run some tests scripts, you will usually see them using pry. that's what I want to show you right now.
If you start typing pry and type return just like we did with irb, you can see that this opens up a full repel environment. We're going to have some nice benefits such as being able to have some color coding and some different things like that.
First we need to require the gem,
require 'bcrypt' and as you can see that gives some nice syntax highlighting and it shows that we have access to the bcrypt library which means we can pull in all of those modules and we can use them however we need.
A lot of times the way that people use bcrypt is for passwords and it is great for that. That's really one of its big focuses. In fact, the model we're going to use is actually called the password module. A lot of people don't realize, they see the word password, you can use it on anything that you need to encrypt. This is how I've used it in the past, let's say that we have a set of social security numbers and we need to have them encrypted in the database.
What we can do is say
ssn = BCrypt::Password.create("555555555") and then we'll call the bcrypt library. The colon which means that we're communicating with the script module and now we want to call the password module.
Now if I hit return then you can see what this does is it goes in it generates this crazy looking long string.
Essentially, this is simply taking the value that we gave it which is that long string of fives and it is now storing that inside of this variable. Our social security number is now encrypted which is pretty cool. Having the Create process is not really the most helpful just by itself, if a user created a password or a social security number, you want the ability to to see if that is still accurate.
In other words you want the ability to test it. Say that they provided their Social Security number and this is a banking application and you're using that as a verification of what they typed in. You can say that they reset their password and they say before you reset your password you have to type in your social security number. Obviously we can't have them typing this whole value and to verify it. what we have is a very helpful method
ssn == "555555555"
Press enter and you can see that that is true. If I try this again
ssn == "555555556" and try six at the very end, you can see that that is false.
This is a very practical example, this is actually exactly what I built it for in an application in the past. It was a financial application where the user in order to make any major changes to the system they had to confirm by typing in their social security number. The application never displayed the Social Security number. it wasn't a problem that this was all that was in the database. in other words I can't call
ssn.show or something like that that would display the values. That would kind of kill the entire point of the encryption algorithm.
Instead, what I could do is I can simply have kind of a one way secure type of communication like we do with these validations. If this seems a little bit like magic it is in a sense, what it does is the people who develop bcrypt actually are overwriting the double equals method right here. That's something that is very powerful. When I was originally learning about bcrypt and teaching myself how it worked this was really confusing to me. I had no idea how you could take this social security number hash this crazy looking thing and say double equals which is you know traditional equals.
In other words if I were to do something like
"asdf" == "asdfasdf" this would return false. This is traditional ruby equivalence but what bcrypt does, if you go through their source code and I did because I wanted to know how exactly this was possible, obviously ssn is not equal to this value. It didn't make any sense how that was working, in the source code but bcrypt actually overrides the double equals method and they pass in some of their encryption algorithms into it so that it can perform a comparison and all it's really doing is it's taking this a value and it's creating another hash essentially and it's saying OK is this equal to this. It's comparing the encrypted values and saying OK is that real, if it is then true if not false. That's kind of a basic way of doing it.
Let's also look at another option, in this case let's say that we were really scared about having phone numbers in the database. We could say
phone = BCrypt::Password.create('555-555-5555', cost: 4). Let's say that we were told we want to have this encrypted in the database so that it's not available in case the database was ever hacked or if it was it was in encrypted form, however, we don't really care about it being incredibly secure. In other words, we care more about the ability to perform this encryption fast. That's something that you may or may not have thought about, in order to create this level of encryption, this took some time. It may have seemed very vast when we just ran it. Imagine that you had an application that had someone new signing up every few seconds. This might get a little bit on the resource intensive side. One of the cool things that the crypt has is the ability to throttle the cost, cost is the level of encryption that we're using. by default it uses 10 but we can use 4 which is the minimum amount that allows.
This actually was much faster for the algorithm to go through. It may not look like this is any less secure but in the world of encryption and security analytics and things like that this type of password would be able to be guessed faster.
To give you a very introductory idea on how hackers work on these kind of things is they will attempt to guess these items. They'll attempt to guess it but the password is going to pass in these values into the encryption algorithm. Essentially they're having to guess is this crazy long set of values.
Technically, if you put all of the various combinations into the system. If you could figure out how long the character string was and you passed in a randomization algorithm that essentially ran as many different combinations as possible, eventually it would be able to crack the password. We could just go hit the system over and over and over and over again until it finally guessed the right password.
When we use cost 4, this could be guessed a little bit faster than what we have up here and that's the reason why by default it has a cost of 10. It's been my experience whenever I encrypted data that the priority if it is something important enough to encrypt the priorities is with the security. I rarely change the cost but it is nice to know that it is there.
There's one more thing that we're going to talk about in this deep dive. Let's say that we don't actually need a password but we need a very long random string of characters. Ruby has some neat random items so you can do
rand 100 and this would give you a number from 0 to 99 in a relatively random order just like this.
Let's say you actually need a string of completely random characters not random integers but random values like this. I've had a few times where I needed that and I actually was able to use the bcrypt gem in order to generate those for me. Even though it usually is used for encryption, there are some components associated with encryption such as what called a salt. Salts allow you to have a very specific kind of algorithm, what a salt does is that it gives a starting point.
If you imagine a randomization algorithm, no computer in the known universe can actually create anything random. The reason is because computers can't think different than they have been programmed to think. There is no such thing as randomization.
One of the problems with that is that algorithms like bcrypt require a level of randomization. The way they accomplish this is by using a different starting point. The concept is called the salt. You start with a salt and that's considered your starting point for all of the encryption. Say that you start with a salt of one. everything you create below that is going to start with that one salt algorithm. It's going to go and create a pattern based off of that. That's how you end up with something like this.
Now with bcrypt password, that all happens behind the scenes that's why we didn't have to create this manually. If you've ever written an application in say pure php, you have to create your own salt and pass it into an encryption algorithm. This thankfully does that for you but it is nice to kind of know how it happens. Say
salt = BCrypt::Engine.generate_salt
You can see that this gives us a set of random characters and it's very different than these other characters. It changes pretty quickly from there. This is something that I've found helpful whenever I needed a completely separate set of random characters. It's a quick and easy way of being able to generate those. Bcrypt is nice and giving that as an added thing, the other reason why I wanted to show you, is to know that there's a lot of things happening in the background to make this possible. It creates almost a domino effect, as soon as this goes in the Create process, it goes and it says OK grab me a salt. Create a random set of characters and that's going to be the starting point for our encryption algorithm. It takes up and then once it has that then it passes it and it runs its algorithm and then it outputs this value and then it gives you access to do things like comparisons with the actual direct values and things like that.
That is our deep dive into encryption, how to encrypt values how to compare encrypted values with real world ones and how to use the crypt gem in Ruby.