Bootcamp
Search…
3.5.3: Password Hashing

Introduction

It is bad security practice to save plain-text passwords in the database. If the database were to get hacked, hackers would get access to everyone's passwords. Instead we'll implement a system to verify passwords by keeping a proxy value in the database that is not the original password. We will do this with a cryptographic hash algorithm.

Hash Algorithm Theory

According to Wikipedia, a cryptographic hash is "a mathematical algorithm that maps data of arbitrary size to a bit array of a fixed size". In other words, a hash function takes a string and irreversibly translates it into another string. Hashing is different from encoding in that hashed values cannot be transformed back to their original value, and encoded values can.

Hash Algorithms are Consistent

The most important aspect of this transformation is that for a given input string, e.g. "hello", the same value will always be output, e.g. "xyz123". We can rely on the fact that when we put "hello" into the function, "xyz123" will always be the output.

Hash Output has Consistent Length

To help obfuscate the value of the input to the hash algorithm, hash output is always the same length, no matter what the length or size of the input.
In our examples we use the SHA512 hash algorithm, which always outputs a message that is 512 bits in size. We can format this output in different ways, but we'll be outputting a hexadecimal number and saving it as a string.
A small change in the input creates a big change in the output. For example, SHA512 hash input "a" generates the following output.
1f40fc92da241694750979ee6cf582f2d5d7d28e18335de05abc54d0560e0f5302860c652bf08d560252aa5e74210546f369fbbbce8c12cfc7957b2652fe9a75
Hash string "A" generates the following output.
21b4f4bd9e64ed355c3eb676a28ebedaf6d8f17bdc365995b319097153044080516bd083bfcce66121a3072646994c8430cc382b8dc543e84880183bf856cff5
Note that the two outputs look completely different. We cannot tell what the input is by looking at the output. The hash obfuscates the original input, and it is mathematically difficult to take the output value and recreate the input value.

Hash Outputs Rarely Repeat for Different Inputs

In hash algorithms, different inputs generating the same output is called a "collision". SHA512 has the ability to convert large inputs into 512-bit outputs, thereby "losing" information, increasing chances of a collision. Fortunately for algorithms such as SHA512, the chances of a collision are sufficiently small.
The creators of hash algorithms mathematically verify the probability of two values producing the same output, minimising that probability as much as possible. Read more here.

Password Hashing Implementation

Summary

  1. 1.
    When the user signs up, hash their password and store only the hashed password in the DB.
  2. 2.
    When the user logs in, hash their password and compare it with the hashed password in the DB.
  3. 3.
    On login, if the hashed passwords match, set an auth cookie on the user's browser. Note that our auth cookie is still insecure in the sense that it can be forged by anyone by manually creating the cookie in the browser. We will see how to secure this cookie in the next module.

jsSHA

jssha is a JavaScript implementation of SHA hash algorithms. Install jssha with NPM.
npm install jssha
Here is an example of how jsSHA can be used.
import jsSHA from 'jssha';
​
const shaObj = new jsSHA('SHA-512', 'TEXT', { encoding: 'UTF8' });
shaObj.update('Text to hash.');
const hash = shaObj.getHash('HEX');
​
console.log('hashed text');
console.log(hash);

SHA512 with User Sign Up

When the user signs up, instead of permanently recording their password in the database we will create a hash of the password, store the hash, and discard the password.
app.post('/signup', (request, response) => {
// initialise the SHA object
const shaObj = new jsSHA('SHA-512', 'TEXT', { encoding: 'UTF8' });
// input the password from the request to the SHA object
shaObj.update(request.body.password);
// get the hashed password as output from the SHA object
const hashedPassword = shaObj.getHash('HEX');
​
// store the hashed password in our DB
const values = [request.body.email, hashedPassword];
pool.query(
'INSERT INTO users (email, hashed_password) VALUES ($1, $2)',
values,
(error, result) => {
// ...
}
);
});

SHA512 with User Log In

When the user logs in we'll hash the incoming value. If the hashed value is the same as the value in the database, it's the same value as the one the user originally entered, so we authenticate this user.
app.post('/login', (request, response) => {
// retrieve the user entry using their email
const values = [request.body.email];
pool.query('SELECT * from users WHERE email=$1', values, (error, result) => {
// return if there is a query error
if (error) {
console.log('Error executing query', error.stack);
response.status(503).send(result.rows);
return;
}
​
// we didnt find a user with that email
if (result.rows.length === 0) {
// the error for incorrect email and incorrect password are the same for security reasons.
// This is to prevent detection of whether a user has an account for a given service.
response.status(403).send('login failed!');
return;
}
​
// get user record from results
const user = result.rows[0];
// initialise SHA object
const shaObj = new jsSHA('SHA-512', 'TEXT', { encoding: 'UTF8' });
// input the password from the request to the SHA object
shaObj.update(request.body.password);
// get the hashed value as output from the SHA object
const hashedPassword = shaObj.getHash('HEX');
​
// If the user's hashed password in the database does not match the hashed input password, login fails
if (user.password !== hashedPassword) {
// the error for incorrect email and incorrect password are the same for security reasons.
// This is to prevent detection of whether a user has an account for a given service.
response.status(403).send('login failed!');
return;
}
​
// The user's password hash matches that in the DB and we authenticate the user.
response.cookie('loggedIn', true);
response.send('logged in!');
});
});

Further Reading

Past students have found this video helpful in introducing cryptographic hashing.