All right.
So now that we've seen what some of the vulnerabilities might be for hashed passwords it's time to level
up and learn about a way that we can prevent these types of dictionary attacks or hash table cracks.
And in order to do that we have to learn about salting.
Now what exactly is salting?
Well, we already know what hashing is.
It's when we take a password, we run it through a hash function and we end up with a hash that we store
on our database.
And as we saw in the last lesson, passwords that are generated by humans are extremely insecure.
They tend to be very short and they tend to be dictionary words that are extremely easy to look up and
create a hash table for. Now salting takes the hashing a little bit further.
In addition to the password, we also generate a random set of characters and those characters along with
the user's password gets combined and then put through the hash function.
So the resulting hash is created from both the password as well as that random unique salt.
So that means no matter how simple the password of the user is adding that salt increases its complexity
increases the number of characters and we make our user database a lot more secure.
So consider the previous problem that we saw in the last lesson where we covered hacking 101.
Now we know that three of our users have the same password and we figured that out because they all
had the same hash.
Now on the other hand, if we had generated a random set of characters which is the salt and combined
the password with the salt in order generate the hash then they won't have the same hash in our user
database.
So let's try and generate Emily's hash from her password.
So we know that her password is qwerty and we generate a random salt.
So then let's go ahead and put in her password, qwerty, and then we append at the end that random salt
that we generated and we end up with a hash.
Now this hash however is not the same as Tony's hash or Angela's hash because the salt is different
each time.
Now the salt is something that the user doesn't have to remember.
And instead it's stored in the database along with the hash.
So that means when the user types in their password when they try to login, you combine their password
with the salt and if you generate the same hash then they must have had the same password.
It's kind of clever isn't it?
And on our database of course we wouldn't be storing their passwords but only the salt and the hash.
Now consider our previous statistics. You can generate about 20 billion MD5 hashes per second.
So even if we added a salt and we made it harder to generate a hash table, with some of the latest
GPUs, you can still probably generate a hash table with all salt combinations relatively quickly.
So what else can we do to increase the security?
Well, we can use something other than MD5
right?
Another hashing algorithm that's valued because it's incredibly slow. And this is where bcrypt comes
in.
This is one of the industry standard hashing algorithms that developers use to keep their users passwords
safe. Because while you can calculate 20 billion MD5 hashes per second,
even the latest and the greatest GPUs in 2019 can still calculate only about 17,000 bcrypt hashes
per second which makes it dramatically harder for a hacker to generate those pre compiled hash tables.
An a salted hash table instead of taking something like three seconds if it was hashed with MD5,
if it was hashed using bcrypt
it would take you something like 8 months, which is not really worth a hacker's while.
They'll probably go and search out a company that has less security enabled. And to make our passwords
even more secure when we're using bcrypt
it has a concept of what's called Salt rounds. How many rounds you're going to salt your password with?
And obviously the more rounds you do the saltier your password and also the more secure it is from hackers.
So what exactly are salt rounds?
Well let's say that our original user password was qwerty and we generate a random set of characters
as the salt.
So now we have qwerty and a random set of salt.
We pass it through our hash function, bcrypt, and we end up with a hash.
Now that's one round of salting.
If we wanted to have two rounds of salting, then we take the hash that was generated in round 1 and we
add the same salt from before. And now we run it through bcrypt the hash function again and we end up
with a different hash. And the number of times you do this is the number of salt rounds.
Now the reason why this is genius is because as computers get faster, remember that Moore's Law says
that every year the number transistors in a computer chip almost doubles and the cost of that faster computer
halves.
So every year you get more computing power for less money.
And this is where salt rounds comes in. When you're hashing your passwords using bcrypt
you can set the number of rounds you want to salt your password.
So that means maybe this year in 2019 you salted 10 rounds but maybe next year you can increase that
number to 12. And for every increase in that number
the amount of time that it takes to hash your password doubles.
And so that means you don't have to change your hashing algorithm or update your code other than simply
changing one number to keep up with the times.
So just to review, coming back to that user database we'll have each user's username stored, we'll have
their randomly generated salt stored and then we'll store their hash after a set number of salting rounds.
And when it comes to checking their password when they login, we'll take the password that they put in
combine it with the salt that's stored in the database and run it through the same number of salting
rounds until we end up with the final hash and we compare the hash against the one that's stored in
the database to see if they've entered the correct password.
So let's give that a go
in real life and implement bscript and salting rounds into our website's authentication.
Now the package that we're going to be using to hash our passwords using bcrypt is also called bcrypt
and you can find the documentation for it as always on NPM.
Now bcrypt is a little bit peculiar about the version compatibility with Node.
So if you're at all in doubt, be sure to first check your Node version by typing "node --version"
or "node -v".
And you can see that currently I'm running the latest stable release of Node which is 10.15
.0 at this current point in time.
Now if you ever want to know what the current stable version is for when you are looking at this video
so this will update weekly or monthly, so by the time you're watching this video this number might already
be higher.
So when you check out the Node version, you can see that depending on your Node version
they also have a bcrypt version that is compatible with that version of Node and they're very specific
about not supporting the unstable versions of Node.
So anything that is a odd number bcrypt is not keen to support because it's less stable, it has more
chances of bugs and they always recommend you to use the latest stable version.
So if you have a older version of Node or if you have even a too new version of Node, how can you go
back to this version that's on the left hand side here on the NOde.js website?
Well you could use something called nvm. And you can install nvm by going to its GitHub repository
and simply copying this line of code and pasting it into your terminal.
And once you've got it installed, restart your terminal, so close it down and open it again, and you should
be able to check your version by writing "nvm --version" and if you get a version back then
that means it's definitely installed and you can use it to update or downgrade your Node version.
So we're going to aim for 10.15.0 or whatever it is that you see when you go on
to nodejs.org,
the long term support version. And we're going to use nvm to install Node on that version by simply
writing "nvm install
10.15.0" and hit enter and it will download and install that latest stable version
of Node for you. Now
once you've got the latest stable version downloaded and installed, you can go ahead and look for the
compatible version of bcrypt.
So at the moment we're on version 10 and bcrypt should be three or above.
Now at this current point in time if I simply try and cd over to my secrets project and run "npm i
bcrypt" then you can see I get some warnings and I suspect this is probably only going to happen for
these few days while the bcrypt team scrambles to try and update their code.
But if you get something similar like I do,
how would you investigate to see how to fix this or whether if you even need to fix it? With every NPM
package there's always a link to the repository where the code is hosted on GitHub. And on GitHub you
can always explore the issues that people are having while they're trying to use this repository. And
you can see that recently 3 days ago, five days ago,
everybody seems to be having issues with installing bcrypt. And if you click on it you'll see a discourse
or a chat or messages between the creator of the repository and people who are experiencing problems. And you
can see whether if you are getting similar problems. Now we don't have as bad as an issue
as this guy because they're actually getting errors and they're not actually getting it installed. And
if you read through this you can see what the solutions are that people are recommending. And at the
point in time where I'm running it the easiest way is to simply install a slightly older version of
bcrypt, 3.0.2 whereas the most current version is 3.0.3.
So in order to install a slightly older version using NPM you still write npm i for install and then
you add in the name of the package and then you have an @ sign and after the @ sign comes the version
that you want.
So I want 3.0.2.
So that is the version that NPM is going to install for me. And you can see that when I change my installation
command I no longer get all of these problems and I have my package successfully installed.
So you might not experiences at all.
You might not get any problems at all other than maybe all two NPM warnings because we don't have a
description or a repository for our packages over here.
But other than that you might actually have no problems.
But if you do experience an issue with bscript or any other package in the future you'll know how to
investigate the issue and see if there are some solutions being offered for that particular issue.
All right, so now that we have bcrypt successfully installed in our project, the next step is to actually
use it.
So we're gonna go ahead and swap out MD5 and instead we're going to be using bcrypt. So bcrypt equals
require bcrypt.
Then we defined the number of salt rounds. So at the moment 10 should be more than sufficient.
And if you remember that the more that you increase the number the harder your computer will also have
to work to generate those hashes.
And if you scroll down it actually shows you how long it will take to generate your hashes or how many
hashes are created depending on how you change this number.
So if you set that salt round number to 31
then on a 2GHz core you can expect to take two days or three days to generate a hash which is which
might look like your app is crashing but it's actually just hashing away.
So at least for 2019 and 2020, I would recommend keeping the salt round at 10. And to actually use
bcrypt,
we're going to use the hash function passing in the password that the user has typed in when they registered
and also the number of rounds of salting we want to do and bcrypt will automatically generate the random
salt and also hash our password with the number of salt rounds that we designed.
So let's go ahead and implement that inside our register post route.
So inside are app.post for register
I'm going to paste those two lines of code that I've copied over from the documentation and inside this
callback function is when we would have generated the final hash after 10 rounds of salting and also
passing in a password.
Now the password that we should pass in here is of course the req.body.password which is what
the user typed in to the password field when they tried to register. And inside this callback function
we'll have access to this hash that we can store into our database.
So I'm going to select the rest of this code and simply paste it in here for it to run and create our
user only once we've generated our hash. And I'm gonna substitute the password field here for the hash
that we generate and then we're going to do exactly the same as before.
We're going to save our new user along with their email and the hashed password into our database.
So let's go ahead and run this and see if it works.
So be sure to remove all the mentions of MD5 if you're going to remove that package from your
project.
You can see here I'm getting a warning. I have in my app.jss on line 12 I've got a console log
that uses my MD5 package which is now of course removed.
So let's go ahead and fix that.
So on line number 12 I've got my console logs so I'm simply going to remove these two lines.
So let's go ahead and hit save and nodemon and restart and you can see now our server has started
without any problems.
So whenever you're removing packages
be careful that you you're not still using it somewhere inside your code.
And let's head over to localhost:3000
let's register a brand new user and this user is going to be user@bcrypthash.com and I'm
completely making up these emails but it just means that when we look at it in our database it will
be easier to see which user was hashed with which hash function.
So this user is a bad user because they're going to use a really simple password 123456.
And we're going to register.
So we've successfully gone over to discover the secret page which means that our user should have been
successfully saved into our database.
So let's go ahead and check it out.
You can see that our last user, the one that we registered just now, has that email user@bcrypthash
.com and their password is quite long
and it is a hash generated using bcrypt as well as 10 rounds of salting.
So if you decide to try and search for this on Google or wherever it may be or any sort of hash table,
rainbow table, whatever it may be, you should not be able to discern the user's original password which
is key.
Now what about when the user tries to login?
So in our login route instead of using MD5 to rehash the password that they've put in we're
going to be using bcrypt. And the way that they tell us to check our password is to use bcrypt.
compare and generate a hash using this line of code.
So let's go ahead and delete this MD5 part because we're no longer using it and instead we're
just pulling in the username and the password that the user typed in on the login page. And then we're
going to look through our database of users and find one where their email matches the user name of
the one that's currently trying to login. And once we've found that person then we have to check to
see if the password that's stored is equal to the password that's currently being entered on the log
in page and this is the part where we're going to substitute with the bcrypt compare method that we
just copied over.
So let's go ahead and paste that into here and we're running this compare method and we're comparing
the plain text password which is the one that the users currently entered in,
so we'll substitute that with password against the hash that we've got stored in our database which
is stored inside the foundUser.password field. Which is basically this over here. And then we get
to check to see if the result is actually true.
Now at this point some of you might have realized that there's a bit of a problem here
right?
Because in this bcrypt compare method the callback that they've defined uses an error and a res.
Now where else have we defined a res? Up here of course.
So in our callback if we want to do res.render inside this bcrypt method, then we have to rename
this something else just so that our code doesn't get confused which res we're trying to use.
So let's go ahead and rename this to result which makes it different from this one which means that
inside here we can check to see if the result of this comparison is equal to true which means that the
password after hashing with the salt is equal to the hash that we've got stored in our database,
then that means that the users got the correct login password and we should res.render our secrets
page.
So let's go ahead and save our code and check that our server is still running without any errors.
And let's go back to our home page and try to log in that user.
So we've got our user@bcrypthash.com and their password was 123456. Hit log in and we get
taken to the secret page which means that that comparison was successful.
So that's it.
We've managed to implement bscript along with salting and 10 rounds of salting at that to massively
secure our user database and make it pretty much impossible to hack using something like a hash table
or dictionary attack. But we can still level up more and we're going to discover that in the next lesson.