How to Generate Cryptographic Hashes
A practical guide to hashing — understand what hash functions do, the difference between MD5, SHA-1, SHA-256, and SHA-512, and how to generate hashes in JavaScript, Python, and the terminal.
What is a hash function?
A cryptographic hash function takes input data of any size and produces a fixed-length output called a digest or hash. The same input always produces the same output, but even a single character change produces a completely different hash. Crucially, it is computationally infeasible to reverse the process — you cannot recover the input from the hash. This makes hashes useful for verifying data integrity, storing passwords (with proper salting), and signing data.
MD5 vs SHA-1 vs SHA-256 vs SHA-512
Different algorithms produce different digest lengths and have different security properties. MD5 and SHA-1 are broken for security purposes — collision attacks are practical. Use SHA-256 or SHA-512 for any security-sensitive application.
Algorithm Output length Status Common uses
───────── ───────────── ────────── ─────────────────────────────
MD5 128 bits (32 hex) Broken File checksums, non-security deduplication
SHA-1 160 bits (40 hex) Broken Legacy systems, git object IDs
SHA-256 256 bits (64 hex) Secure TLS certificates, JWT signing, file integrity
SHA-512 512 bits (128 hex) Secure Higher-security applicationsGenerating hashes in JavaScript
The Web Crypto API (available in all modern browsers and Node.js 15+) provides SubtleCrypto.digest() for SHA-family hashes. For Node.js, the built-in crypto module is simpler to use.
// Browser / Node.js 15+ — Web Crypto API
async function sha256(message) {
const msgBuffer = new TextEncoder().encode(message);
const hashBuffer = await crypto.subtle.digest("SHA-256", msgBuffer);
const hashArray = Array.from(new Uint8Array(hashBuffer));
return hashArray.map(b => b.toString(16).padStart(2, "0")).join("");
}
const hash = await sha256("hello world");
console.log(hash);
// "b94d27b9934d3e08a52e52d7da7dabfac484efe04294e576..."
// Node.js — crypto module (simpler)
import { createHash } from "crypto";
createHash("sha256").update("hello world").digest("hex");
// same resultGenerating hashes in Python
Python's built-in hashlib module supports MD5, SHA-1, SHA-256, SHA-512, and more. Always encode the string to bytes before hashing.
import hashlib
message = "hello world"
# SHA-256
sha256 = hashlib.sha256(message.encode()).hexdigest()
print(sha256)
# "b94d27b9934d3e08a52e52d7da7dabfac484efe04294e576..."
# MD5
md5 = hashlib.md5(message.encode()).hexdigest()
print(md5) # "5eb63bbbe01eeed093cb22bb8f5acdc3"
# Hash a file
import hashlib
with open("file.zip", "rb") as f:
file_hash = hashlib.sha256(f.read()).hexdigest()
print(file_hash)Generating hashes from the command line
macOS and Linux ship with shasum and md5sum (or md5 on macOS). These are useful for verifying downloaded file checksums.
# SHA-256 of a string
echo -n "hello world" | shasum -a 256
# b94d27b9934d3... -
# SHA-256 of a file
shasum -a 256 file.zip
# MD5
md5 file.zip # macOS
md5sum file.zip # Linux
# SHA-512
echo -n "hello" | shasum -a 512Hashing passwords — use bcrypt, not SHA-256
SHA-256 is fast, which makes it bad for password hashing — an attacker can try billions of guesses per second. Password hashing requires a slow, salted algorithm designed to resist brute force: bcrypt, Argon2, or PBKDF2. Never store plain SHA-256 of passwords in a database.
// Node.js — bcrypt
import bcrypt from "bcrypt";
// Hash a password (cost factor 12 = ~250ms on modern hardware)
const hash = await bcrypt.hash("user_password", 12);
// Verify
const match = await bcrypt.compare("user_password", hash); // true
// Python — bcrypt
import bcrypt
hashed = bcrypt.hashpw(b"user_password", bcrypt.gensalt(rounds=12))
bcrypt.checkpw(b"user_password", hashed) # TrueCan two different inputs produce the same hash?
Theoretically yes — this is called a collision. For MD5 and SHA-1, practical collision attacks exist. For SHA-256 and SHA-512, no collision has ever been found and finding one is considered computationally infeasible.
If hashing is one-way, how are passwords verified?
You hash the input the user provides at login and compare it to the stored hash. If they match, the password is correct. You never store the original password.
What is a salt and why does it matter for password hashing?
A salt is a unique random value added to the password before hashing. It ensures that two users with the same password get different hashes, and prevents precomputed rainbow table attacks.
How to Parse a JWT
A practical guide to reading and decoding JSON Web Tokens — understand the three-part structure, decode the header and payload, and know what to check before trusting a token.
Read guide →How to Hash a Password Correctly
A practical guide to storing passwords securely — why plain hashing is wrong, which algorithms to use, how salting works, and what a safe implementation looks like.
Read guide →How to Sign API Requests with HMAC
A practical guide to HMAC request signing — what it proves, how to construct a canonical request, sign it with a shared secret, and verify it on the server.
Read guide →How OAuth 2.0 Works
A practical guide to OAuth 2.0 — the authorization code flow, PKCE, tokens, scopes, and when to use each grant type.
Read guide →