CodingNic

Core Node.js Modules

The crypto Module: Hashing and Random Values

Core Node.js Modules 12 min read

The crypto Module: Hashing and Random Values

Objectives

By the end of this lesson, you should be able to:

  • Hash a value with crypto.createHash
  • Generate secure random values with crypto.randomBytes and crypto.randomUUID
  • Hash and verify a password with a salt using crypto.scryptSync

💡 Why this matters: Storing a user’s password in plain text is a serious security failure. Node’s built-in crypto module provides everything needed to hash values and generate secure random data, no external package required, and this exact pattern reappears once user authentication is built in a later course.

⚠️ A note on verification: every snippet and output in this lesson was actually run with Node.js.

Hashing a Value

javascript
const crypto = require('crypto');

const hash = crypto.createHash('sha256').update('hello world').digest('hex');
console.log('sha256 hash:', hash);
text
sha256 hash: b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9

createHash('sha256') creates a hash object using the SHA-256 algorithm, .update(value) feeds it the data to hash, and .digest('hex') produces the final result as a hex string. Hashing is one-way, there’s no way to recover 'hello world' from the hash, and hashing the exact same input always produces the exact same output, this determinism is why hashing alone isn’t enough for passwords, covered next.

Random Values

javascript
const crypto = require('crypto');

const uuid = crypto.randomUUID();
console.log('uuid:', uuid);

const randomHex = crypto.randomBytes(8).toString('hex');
console.log('random bytes (hex):', randomHex);
text
uuid: c846c8a0-0b95-414b-b411-b6e7f61188e9
random bytes (hex): fda70a0cc96e108c

crypto.randomUUID() generates a standard-format unique identifier, useful anywhere a unique ID is needed (a database record, a session token) without relying on a database to assign one. crypto.randomBytes(n) generates n cryptographically secure random bytes, .toString('hex') renders them as a readable hex string. Both use a source of true randomness suitable for security purposes, unlike Math.random(), which is not secure enough for anything sensitive.

Hashing Passwords Correctly

Hashing a password directly with createHash is not safe, identical passwords produce identical hashes, and fast hash algorithms like SHA-256 can be brute-forced quickly. The standard fix is a salt, random data mixed into each password before hashing, combined with a deliberately slow algorithm:

javascript
const crypto = require('crypto');

function hashPassword(password) {
  const salt = crypto.randomBytes(16).toString('hex');
  const hash = crypto.scryptSync(password, salt, 64).toString('hex');
  return `${salt}:${hash}`;
}

function verifyPassword(password, stored) {
  const [salt, originalHash] = stored.split(':');
  const hash = crypto.scryptSync(password, salt, 64).toString('hex');
  return hash === originalHash;
}

const stored = hashPassword('correct horse battery staple');
console.log('correct password:', verifyPassword('correct horse battery staple', stored));
console.log('wrong password:', verifyPassword('wrong guess', stored));
text
correct password: true
wrong password: false

scryptSync(password, salt, keylen) is deliberately slow and memory-intensive, making brute-force guessing impractical. Storing salt:hash together (the salt itself isn’t secret) lets verifyPassword re-hash a login attempt with the same salt, and compare the result, this is exactly how real authentication systems check a password without ever storing it directly.

Try It

  1. Hash the strings 'apple' and 'Apple' with createHash('sha256'), and confirm the hashes are completely different despite the tiny input difference.
  2. Generate five UUIDs in a loop with crypto.randomUUID(), and confirm every one is different.
  3. Write the hashPassword/verifyPassword pair yourself from scratch, then test it with a correct password, an incorrect password, and an empty string.
  4. Explain, in your own words, why a salt is necessary even though scryptSync is already a strong hashing algorithm.

Recap

  • createHash(algorithm).update(value).digest('hex') produces a one-way hash of a value.
  • randomUUID() and randomBytes(n) generate cryptographically secure random values.
  • Passwords should be hashed with a per-password salt and a slow algorithm like scryptSync, never with a fast hash alone.

Next lesson: events, Node’s event emitter pattern.