Learn Node.js
Access all our written and video Node.js tutorials.
No matter how many precautions you take, you can never assume a database is impenetrable. Because cybercriminals use an array of resources in cyber attacks, a key step in password security is salting and hashing. In this guest tutorial by Michelle Selzer (@codingCommander), learn how to salt and hash a password using bcrypt.
It is crucial to keep users’ passwords secure to protect against cyber attacks. The first step is storing passwords on a secure database server. However, no matter how many precautions you take, you can never assume the database is impenetrable. Cybercriminals can use an array of resources and may even collaborate with one of your coworkers. That is why you must take a second step to make deciphering any stolen passwords much harder: salting and hashing.
In this tutorial we’ll:
By the end of this tutorial, you will know how to use bcrypt to keep user passwords secure.
Safely store user passwords using bcrypt.
“Hashing” a password refers to taking a plain text password and putting it through a hash algorithm. The hash algorithm takes in a string of any size and outputs a fixed-length string. No matter the size of the original string (i.e., the plain text password), the output (the hash) is always the same length. Since the same process is always applied, the same input always yields the same output.
Say the plain text password is Ralph$467
. Every time you pass Ralph$467
into the hash algorithm, the returned hash is the same. If someone else’s plain text password is jsu*^7skdl230H98
, the length of its hash is the same as for Ralph$467
.
Because hash algorithms always produce the same result for a specific password, they are predictable. If you only hash the password, a hacker can figure out the original password. Hashing is not enough.
A salt is a random string. By hashing a plain text password plus a salt, the hash algorithm’s output is no longer predictable. The same password will no longer yield the same hash. The salt gets automatically included with the hash, so you do not need to store it in a database.
Not all hash algorithms are created equal, and there are many options available to NodeJS developers. Many developers are unsure about which algorithm to use. Let’s be clear that there is not only one correct choice. We can evaluate available algorithms’ security based on certain requirements. Let’s see if bcrypt meets these requirements.
Bcrypt uses the Blowfish cypher. One criterion we want our hash algorithm to meet is speed. You don’t want the algorithm to run too fast. We won’t get into a detailed discussion about hashing speeds and cyberattacks, but just know that Blowfish is slow enough to prevent certain attacks.
As discussed earlier, hashing a password is not enough. We must also salt the password, and bcrypt requires you to do so. Random bytes get added to the password, and together the salted hash meets security recommendations on length and unpredictability.
Another aspect to look at is longevity versus record. Bcrypt is widely used and has been around for many years (it was created in 1999). Reported issues are scarce, and no one has broken the Blowfish algorithm. It is still essential to stay on top of reported bugs and security issues.
$ npm i bcrypt
Now let’s look at the code.
To use bcrypt, we must include the module.
const bcrypt = require ('bcrypt');
saltRounds
Next, we set the saltRounds
value. The higher the saltRounds value, the more time the hashing algorithm takes. You want to select a number that is high enough to prevent attacks, but not slower than potential user patience. In this example, we use the default value, 10
.
const saltRounds = 10;
Next, for simplicity, we hard-code a user password. In real life, this would be a value passed back from a registration form.
var password = "Fkdj^45ci@Jad";
You can salt and hash the password in one function or by using separate functions. In our first example, we separate the two functions. You can also salt and hash synchronously or asynchronously. The recommendation is to do so asynchronously, so that is the method used in this tutorial.
Below is the genSalt
function, used to generate a salt.
We pass bcrypt.genSalt()
these parameters:
saltRounds
bcrypt.genSalt(saltRounds, function(err, salt) { // returns salt});
We now add the hash function inside genSalt.
We pass bcrypt.hash() these parameters:
bcrypt.genSalt(saltRounds, function(err, salt) { bcrypt.hash(password, salt, function(err, hash) { // returns hash console.log(hash); });});
In the above example, we simply console.log
the hash. In real life, there would probably be a function there to insert the hash into a database.
bcrypt.genSalt(saltRounds, function(err, salt) { bcrypt.hash(password, salt, function(err, hash) { // Store hash in database here });});
When we put all the steps together, we get:
```jsconst bcrypt = require ('bcrypt');
const saltRounds = 10;var password = "Fkdj^45ci@Jad";
bcrypt.genSalt(saltRounds, function(err, salt) { bcrypt.hash(password, salt, function(err, hash) { // Store hash in database here });});
The variable hash is returned, and we can store it in our database.
Instead of writing the salt and hash functions separately, we can combine them into one function.
bcrypt.hash(password, saltRounds, function(err, hash) { // Store hash in database here});
The above example gives the same result as the code below
bcrypt.genSalt(saltRounds, function(err, salt) { bcrypt.hash(password, salt, function(err, hash) { // Store hash in database here });});
Now that we’ve safely secured the hash in our database, when a user attempts to log in, we have to compare the plain text password to the hash. We do so by using the bcrypt compare
function.
We pass bcrypt.compare()
these parameters:
bcrypt.compare(password, hash, function(err, result) { // returns result});
In the example below, we hard-coded a password to compare to the hash. If the password matches, we print “It matches!” and if not, we print “Invalid password!”.
var password2 = "djlfhjd(456";bcrypt.compare(password2, hash, function(err, result) { if (result) { console.log("It matches!") } else { console.log("Invalid password!"); }});
If we want to check to make sure the code works before querying the hash value, we can put the code inside the callback of our original hash function.
const bcrypt = require ('bcrypt'); // require bcrypt
const saltRounds = 10; // Data processing speedvar password = "Fkdj^45ci@Jad"; // Original Passwordvar password2 = "djlfhjd(456";bcrypt.hash(password, saltRounds, function(err, hash) { // Salt + Hash bcrypt.compare(password2, hash, function(err, result) { // Compare // if passwords match if (result) { console.log("It matches!") } // if passwords do not match else { console.log("Invalid password!"); } });});
If we run the code in the example above, the output is: Invalid password!
In real life, the compare function would not execute inside the hash callback. The example is just for learning and testing purposes. Also, instead of printing a statement, the user would either authenticate or not authenticate. If the passwords match, the user can log in. If they do not match, the user is denied access.
bcrypt.compare(password2, hash, function(err, result) { if (result) { // log in } else { // access denied }});
In this example, we salt and hash the password, then store it in a PostgreSQL database.
const db = require('./db.js'); // connection variableconst bcrypt = require ('bcrypt'); // bcrypt
const saltRounds = 10; // data processing time
var username = "starbuck";var password = "ldfgkj78%^&appdKO039*";
// query statement to store hashvar statement = "UPDATE user_table SET password = $1 WHERE username = $2";
// salt, hash, and storebcrypt.hash(password, saltRounds, function(err, hash) { let values = [hash, username]; // query values // store hash in database db.query(statement, values, function(err,res) { if (err) throw err; else { console.log("stored!"); } });});
In the example below, we query a stored hash from a PostgreSQL database. Once we retrieve that value, we compare it to a password and evaluate if the user can log in.
const db = require('./db.js'); // connection variableconst bcrypt = require ('bcrypt'); // bcrypt
var username = "starbuck";var password = "ldfgkj78%^&appdKO039*";// statement to query the user’s passwordvar statement = "select password from user_table where username = $1";var values = [username]; // query values
// function to log infunction hasAccess(result){ if (result) { // insert login code here console.log("Access Granted!"); } else { // insert access denied code here console.log("Access Denied!"); }}
// query database for user's passworddb.query(statement, values, function(err, res) { if (err) throw err; else { var hash = res.rows[0].password; // compare hash and password bcrypt.compare(password, hash, function(err, result) { // execute code to test for access and login hasAccess(result); }); }});
For security purposes, it is essential to salt and hash passwords before storing them in a secure database. Hashing algorithms turn a plain text password into a new fixed-length string called a hash. Before hashing a password, we apply a salt. A salt is a random string that makes the hash unpredictable.
Bcrypt is a popular and trusted method for salt and hashing passwords. You have learned how to use bcrypt’s NodeJS library to salt and hash a password before storing it in a database. You have also learned how to use the bcrypt compare function to compare a password to a hash, which is necessary for authentication.
Learn Node.js
Access all our written and video Node.js tutorials.