How to Use Mysql Password Encryption with NodeJS, Express and Bcrypt

crashdaddy
5 min readDec 10, 2020
Dave’s not here, man

Note: this article begins after you’ve setup and coded an Express Router on a NodeJS server and written an API that connects to a mysql database

Now that we’ve got our sexy app up and running, we want to make it a little more sophisticated. We didn’t bother to include user-password encryption before, because who cares about hacking my demo app for school, right?

Everrryyyyyybbboooodddddddyyyyyy!!!!

We’re going to be using a package called bcrypt to encrypt our password and for the validation. Here’s what wikipedia has to say about bcrypt:

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.

So it’s been around a lot longer than you might figure, but the algorithm is still available to you in React by including the bcrypt package in your project:

npm install bcrypt

Now in our controllers, where we keep the sql queries for the API, we’re going to edit whatever file our login functions exist in and add the following at the top:

const bcrypt = require('bcrypt');
const saltRounds = 10;

A saltRound is

With “salt round” they actually mean the cost factor. The cost factor controls how much time is needed to calculate a single BCrypt hash. The higher the cost factor, the more hashing rounds are done. Increasing the cost factor by 1 doubles the necessary time. The more time is necessary, the more difficult is brute-forcing.

So we chose 10, which is a pretty big number for a small app, but it’s also not big enough to cause any noticeable lag. Play around with it if you want, but unless you’re building an enterprise app with millions of users, you don’t have to go much further.

Important: This implementation will not affect any passwords that are already stored in the database as plain text. In fact, any passwords that already exist will not work any more!

One thing you may have to do is to change the datatype of the password column in your mysql table. For instance, if you just set it up as char(25) to hold a text password, you may have to alter it to hold the hashed password variable.

Here’s an excellent resource to remind you how to alter a column’s datatype in a mysql table if you need it.

Encrypting a Password

We want to use our encryption when the person first registers their account, so just find whatever function you wrote to register a new user and here’s how you include encryption:

const register = async function(req,res){    const password = req.body.password;    
const encryptedPassword = await bcrypt.hash(password, saltRounds)
let users={
"user_name":req.body.userName,
"email_address":req.body.email,
"password":encryptedPassword
}
pool.query('INSERT INTO players SET ?',users, function (error, results, fields) { if (error) {
res.send({
"code":400,
"failed":"error occurred",
"error" : error})
} else {
res.send({
"code":200,
"success":"user registered sucessfully"
});
}
});
}

As you can see, all we did was instead of using the exact text the user typed, we take the password the user chose and ran it through bcrypt into a new password called encryptedPassword with this:

const password = req.body.password;    
const encryptedPassword = await bcrypt.hash(password, saltRounds)

Now, instead of “hunter2” the user’s password in the database is going to look something like “e38ad214943daad1d64c102faec29de4afe9da3d”

The user will never see that password and your database administrator (you) will never see anything else.

Retrieving the Password

Now when the user wants to login to your app, they’re still going to want to type “hunter2” in the password field. And who can blame them? That’s the password they chose.

So in your function that logs the user in to the database we’re going to do something like this:

const login = async function(req,res){    
var email= req.body.email;
var password = req.body.password;
pool.query('SELECT players.*, count(history.userId) AS gamesPlayed FROM players LEFT JOIN history ON history.userId=players.id WHERE email_address = ?',[email], async function (error, results, fields) { if (error) { res.send({
"code":400,
"failed":"error occurred",
"error" : error
})
}else{
if(results.length >0){
const comparison = await bcrypt.compare(password, results[0].password) if(comparison){
res.send({
"code":200,
"success":"login successful",
"id": results[0].id,
"userName": results[0].user_name,
"score": results[0].score,
"gamesPlayed": results[0].gamesPlayed,
"boardPref": results[0].boardPref
})
}else{
res.send({
"code":204,
"error":"Email and password does not match"
})
}
} else{
res.send({
"code":206,
"error":"Email does not exist"
});
}
}
});
}

We’re using the email address and password to identify the user, so we pull the record with a matching email address. We could have just as easily used the username instead, but this app we’re building only allows each email address to be used once also, so that’s why we’re using email.

This call retrieves the encrypted password associated with that email.

Then what we do is encrypt the password they entered when logging in

const comparison = await bcrypt.compare(password, results[0].password)

Where the “password” variable comes from taking the password the user entered:

var password = req.body.password;

and results[0].password just refers to the record returned from the database.

And all we’re doing is hashing that password they used when logging in and then checking to see if it matches the already hashed password we’ve stored when they registered. If it does match, that’s great, and we send back a success message and the user’s info.

if(comparison){              
res.send({
"code":200,
"success":"login successful",
"id": results[0].id,
"userName": results[0].user_name,
"score": results[0].score,
"gamesPlayed": results[0].gamesPlayed,
"boardPref": results[0].boardPref
})
}

If not, we just send back an error message and they can try again.

else{            // if 'comparison' is false
res.send({
"code":204,
"error":"Email and password does not match"
})
}
} else{
res.send({
"code":206,
"error":"Email does not exist"
});
}
}
});

Done and Done

That’s all there is to it — for our immediate purposes anyway. Later when your app blows up you’ll want to make it even more sophisticated, like allowing a user to have several accounts with the same email and just checking by username, encrypting their email address and other personal info, etc…

Now if someone hacks your database, all they’ll get is a bunch of “e38ad214943daad1d64c102faec29de4afe9da3d” looking nonsense, and good luck trying to figure out exactly what word and how many salt rounds you have to go through to get to that mess. And your users can still use “password” as their password.

Because you know they’re gonna

--

--

crashdaddy

4a 75 73 74 20 61 6e 6f 74 68 65 72 20 63 6f 6d 70 75 74 65 72 20 6e 65 72 64 20 77 69 74 68 20 61 20 62 6c 6f 67