Skip to content

Node.js crypto Module

The built-in crypto module gives Node.js access to hashing, encryption, and secure randomness without any external dependency. This lesson covers the pieces you'll use most often building real applications.

What Is the crypto Module?

crypto wraps OpenSSL's cryptographic functionality and exposes it to JavaScript: one-way hashing (createHash), message authentication codes (createHmac), symmetric encryption/decryption, and cryptographically secure random value generation (randomBytes, randomUUID).

It's the foundation underneath higher-level security features covered later in this course, generating secure session IDs, signing webhooks with HMAC, and (indirectly) supporting password hashing libraries like bcrypt.

import crypto from 'node:crypto';

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

const id = crypto.randomUUID();
console.log('Random UUID:', id);

createHash('sha256') is deterministic, the same input always produces the same hash, which is exactly why it should never be used alone for passwords (see the Password Hashing lesson for why).

Core crypto Methods

crypto.createHash(algorithm).update(data).digest(encoding)
crypto.createHmac(algorithm, secret).update(data).digest(encoding)
crypto.randomBytes(size)
crypto.randomUUID()
  • createHash() produces a one-way digest, useful for checksums, not for passwords.
  • createHmac() produces a keyed hash, proving data hasn't been tampered with by someone who doesn't know the secret.
  • randomBytes(size) generates cryptographically secure random bytes, unlike Math.random().
  • randomUUID() generates a standards-compliant random UUID (v4) in one call.

crypto Module Cheatsheet

Common cryptographic tasks and the crypto methods that handle them.

Task Method
Hash data (checksum) crypto.createHash('sha256')
Sign/verify with a secret crypto.createHmac('sha256', secret)
Secure random bytes crypto.randomBytes(32)
Random UUID crypto.randomUUID()
Password hashing Use bcrypt/argon2, not raw crypto
Symmetric encryption crypto.createCipheriv()/createDecipheriv()

Hashing vs Encryption: Not the Same Thing

Hashing is one-way: createHash() transforms data into a fixed-length digest that cannot be reversed back into the original input. Encryption is two-way: data encrypted with createCipheriv() can be decrypted back to its original form using the correct key. Confusing the two is a common and serious security mistake.

  • Use hashing for checksums, integrity checks, and (with a proper algorithm) password storage.
  • Use encryption when you need to recover the original data later (e.g. storing an API key you'll need to send back to a third party).
  • Never "encrypt" passwords, hash them with a slow, salted algorithm instead.

Verifying Webhooks With HMAC

A very common real-world use of crypto is verifying that a webhook payload actually came from the service that claims to have sent it (Stripe, GitHub, etc.), by comparing an HMAC signature computed with a shared secret.

import crypto from 'node:crypto';

function verifySignature(payload, receivedSignature, secret) {
  const expected = crypto
    .createHmac('sha256', secret)
    .update(payload)
    .digest('hex');

  return crypto.timingSafeEqual(
    Buffer.from(expected),
    Buffer.from(receivedSignature)
  );
}

crypto.timingSafeEqual() compares two buffers in constant time, preventing timing attacks that a plain === comparison would be vulnerable to.

Common Mistakes

  • Using crypto.createHash() alone (without salting and a slow algorithm) to store user passwords.
  • Using Math.random() instead of crypto.randomBytes()/randomUUID() for anything security-sensitive.
  • Comparing signatures with === instead of crypto.timingSafeEqual(), opening the door to timing attacks.
  • Hardcoding encryption keys or HMAC secrets directly in source code instead of environment variables.

Key Takeaways

  • The crypto module provides hashing, HMAC signing, encryption, and secure randomness built into Node.js.
  • Hashing is one-way; encryption is two-way, and they solve different problems.
  • randomBytes()/randomUUID() should replace Math.random() for anything security-related.
  • HMAC signatures, compared with timingSafeEqual(), are the standard way to verify webhook authenticity.

Pro Tip

Never write your own password hashing scheme on top of raw crypto.createHash(). Use a dedicated, battle-tested library like bcrypt or argon2, covered in the Password Hashing lesson, they handle salting and slow-hashing correctly by default.