โ† Back to Blog

How to Hash Passwords Securely

2026-04-08 ยท 5 min read

Never Use MD5 or SHA256 for Password Storage

This is the most important rule in password security: general-purpose hash functions like MD5, SHA1, and SHA256 should never be used for password storage. The reason is they're designed to be too fast โ€” capable of calculating billions of hashes per second. This means after obtaining hashes, attackers can find original passwords through brute force or dictionary attacks in very short time. In the 2012 LinkedIn data breach, about 60% of SHA1 password hashes were cracked within days because many users used weak passwords.

Special Requirements for Password Hashing

Secure password storage needs to meet three core requirements:

bcrypt: The Classic Password Hash Function

bcrypt was designed by Niels Provos and David Maziรจres in 1999 and is the most widely used password hash function today. It has built-in salt and cost factor, controlling computational complexity through the cost parameter:

// Node.js (using bcryptjs or bcrypt package)
const bcrypt = require('bcryptjs');

// Hashing a password
async function hashPassword(password) {
  const saltRounds = 12; // Cost factor: 2^12 = 4096 iterations
  const hash = await bcrypt.hash(password, saltRounds);
  return hash;
  // Returns: "$2b$12$[22-char salt][31-char hash]"
}

// Verifying a password
async function verifyPassword(password, hash) {
  const match = await bcrypt.compare(password, hash);
  return match; // true or false
}

// Usage
const hash = await hashPassword('mypassword123');
// Store hash in database

const isValid = await verifyPassword('mypassword123', hash);
// true

Cost parameter recommendation: For most applications, 12โ€“14 is appropriate (making hash time approximately 100โ€“300ms). As hardware improves, gradually increase this value.

Argon2: The Modern First Choice for Password Hashing

Argon2 won the 2015 Password Hashing Competition (PHC) and is considered the most secure password hashing algorithm today. Argon2 has three variants: Argon2i (resistant to side-channel attacks), Argon2d (resistant to GPU attacks), and Argon2id (combination of both, recommended). Compared to bcrypt, Argon2 can also configure memory usage, making it harder to crack in parallel with specialized hardware (ASIC).

// Python (using argon2-cffi)
from argon2 import PasswordHasher

ph = PasswordHasher(
    time_cost=2,          # Iterations
    memory_cost=65536,    # 64 MB RAM usage
    parallelism=2,        # Parallel threads
    hash_len=32,          # Hash output length
    salt_len=16           # Salt length
)

# Hash
hash = ph.hash("mypassword123")
# Returns: "$argon2id$v=19$m=65536,t=2,p=2$..."

# Verify
try:
    ph.verify(hash, "mypassword123")
    print("Password correct")
except:
    print("Password incorrect")

PBKDF2: The Widely Supported Standard

PBKDF2 (Password-Based Key Derivation Function 2) is a NIST-recommended and FIPS 140-2 approved password hashing scheme. While not as secure as bcrypt and Argon2 (no memory complexity), it's very useful in environments requiring compliance certification:

// Node.js (built-in crypto module)
const crypto = require('crypto');
const util   = require('util');
const pbkdf2 = util.promisify(crypto.pbkdf2);

async function hashPassword(password) {
  const salt = crypto.randomBytes(32).toString('hex');
  const hash = await pbkdf2(password, salt, 310000, 32, 'sha256');
  return `${salt}:${hash.toString('hex')}`;
}

async function verifyPassword(password, storedHash) {
  const [salt, hash] = storedHash.split(':');
  const newHash = await pbkdf2(password, salt, 310000, 32, 'sha256');
  return crypto.timingSafeEqual(Buffer.from(hash, 'hex'), newHash);
}

Timing-Safe Comparison

When verifying passwords (or any secret values), you must use a timing-safe (constant-time) comparison function, not regular string equality (like === or ==). Regular string comparison returns at the first mismatching character, allowing attackers to infer hash content by measuring response times (timing attack). Node.js provides crypto.timingSafeEqual(), and bcrypt's compare() also has built-in timing-safe comparison.

Password Hashing Best Practices Summary

Try the free tool now

Use Free Tool โ†’