Hashing

In this section we shall look at hashing, hashing algorithms, and verifying user input against a hash.

Hashing / Encryption

Dealing with keeping confidentiality of text traversing any network we have to recognize some of the terms of the domain. Cryptography[17] sometimes also called Cryptology

is the practice and study of techniques for secure communication in the presence of third parties called adversaries.
In cryptography, ciphertext or cyphertext is the result of encryption performed on plaintext using an algorithm, called a cipher.[18]

Decryption is the opposite process, ie converting ciphertext into plaintext. If this is done securely. Alice[19] may encrypt a message in plaintext into ciphertext, send it across the network to Bob, who may decrypt it back into plaintext by a key, and then read it. Any eavesdropping on the message while it is traversing the net will only give Eve, the eavesdropper, some un-understandable ciphertext. The message is confidential.

Hashing is in some respects similar, and in others, fundamentally different.

A cryptographic hash function (CHF) is a hash function that is suitable for use in cryptography. It is a mathematical algorithm that maps data of arbitrary size (often called the "message") to a bit string of a fixed size (the "hash value", "hash", or "message digest") and is a one-way function …

The last bit, the practical irreversibility of hash functions is exactly why they are to be used for passwords. There's no key to get for Eve. The only way to crack a hash is brute force, ie guessing the plaintext work producing the hash. The quality, strength, of the CHF is dependent on the time it will take a modern computer to find the plaintext by brute force. Here we are talking years, many years.

As you already know, we use hashing for obfuscating passwords beyond recognition before we store them. Encryption would entail the possibility of decryption. We don't want that. We don't need that. To crack hashed passwords you will need to hash a string of cleartext word, match it against the stored password, and, if there's no match, try another, then another, then … It will not help you that you might know the hashed representation of the password.

All web programming languages have functions for hashing with an array of different hashing algorithms. When we make a choice of one, choose a good one, it has some repercussions on other parts of whatever software you are building. It affects, for example, the layout of the database user table or collection you are authenticating your users against.

It should also be stated that unless you are totally sure of what you are doing, do NOT use home made hashing, with or without salt. It is not secure. Instead you should apply publicly known and recognized functions from whatever language you use for your applications.

Example 47.1. Hashing Example, Fragment I of myg172/routes/index.js

In order to work with hashing and encryption we have to get the necessary software. The two essential modules for node are bcryptjs, and crypto-js. You just need one of them, for hashing. bcryptjs is best practice. The other one, crypto-js has other, and also standardized algorithms. It also has encryption functions. Let us see them in practice:

const bcrypt = require('bcryptjs');                 // added for bcrypt hashing
const sha256 = require('crypto-js/sha256');         // added for encryption and other hashes
const sha512 = require('crypto-js/sha512');         // added for encryption and other hashes
const md5 = require('crypto-js/md5');               // added for encryption and other hashes
const sha1 = require('crypto-js/sha1');             // added for encryption and other hashes
const sha3 = require('crypto-js/sha3');             // added for encryption and other hashes

Usage of a series of the hashing functions is part of the following example.

Example 47.2. Hashing Example, Fragment II of myg172/routes/index.js
const bchash = async function (req, algo='bcrypt') {
    let hash;
    switch (algo) {
        case 'md5':
            hash = await String(md5(req.body.password));
            break;
        case 'sha1':
            hash = await String(sha1(req.body.password));
            break;
        case 'sha3':
            hash = await String(sha3(req.body.password));
            break;
        case 'sha256':
            hash = await String(sha256(req.body.password));
            break;
        case 'sha512':
            hash = await String(sha512(req.body.password));
            break;
        default:
            hash = await bcrypt.hash(req.body.password, 10);
            break;
    }
    let obj = {
        algo: algo,
        length: hash.length,
        digest: hash
    };
    return obj;
}

Figure 47.1. A Sample Output from Various Hashing Algorithms
A Sample Output from Various Hashing Algorithms

Please notice that the four instances of using the bcrypt algorithm of the bcrypt.hash function results in four different hashes, yet they all verify as correct with bcrypt.compare.

Verify the Hash

Example 47.3. The bcrypt Case. Fragment of nodeAuthDemo/models/handleUsers.js
exports.verifyUser = async function (req) {
    let check = { email: req.body.email };
    let u = await this.getUsers(check);         // here the database is read
    let success = await bcrypt.compare(req.body.password, u[0].password);
                                                // u[0].password was read in the database
    return success;
}

Just One More Thing on Hashing

The passwords used by your operating system are also interesting in order to get some more perspective on hashing. Regarding Linux especially, but not only, you might want to read https://crypto.stackexchange.com/questions/40841/what-is-the-algorithm-used-to-encrypt-linux-passwords and, for a deeper theoretical background: https://security.stackexchange.com/questions/211/how-to-securely-hash-passwords



[17] https://en.wikipedia.org/wiki/Cryptography
[18] https://en.wikipedia.org/wiki/Ciphertext
[19] https://en.wikipedia.org/wiki/Alice_and_Bob