←Back to Guides
๐Ÿ›ก๏ธSecurity & Cryptographyโฑ๏ธ 15 min read•Updated October 2026

Password Storage Security: Argon2id, Bcrypt, Salting & NIST Guidelines

The authoritative production guide to modern password security: Why fast hashes (MD5, SHA-256) are fatal for password storage, how Argon2id and Bcrypt defeat GPU attacks, tuning work factors, and NIST SP 800-63B rules.

#Security#Bcrypt#Argon2#Passwords#Cryptography#Hashing#NIST

1. Fast Hashes vs Slow Hashes: The Fundamental Law

Cryptographic hash functions like SHA-256, SHA-512, and MD5 were designed to be extremely fast to verify file integrity and sign blockchain transactions. When applied to passwords, speed is catastrophic: a consumer NVIDIA RTX 4090 GPU can compute roughly 22 billion SHA-256 hashes per second (about 160 billion for MD5), exhausting every lowercase 8-character password in seconds and every printable-ASCII 8-character password in a few days.
Hash AlgorithmDesign GoalGPU Hash Rate (RTX 4090, approx.)Password SuitabilityCopy
MD5Legacy checksum~160 billion/secCatastrophic: broken and trivially brute-forced
SHA-256Data integrity & signatures~22 billion/secUnacceptable for passwords: far too fast
Bcrypt (Cost 12)Password storage (CPU-hard)~1,400/sec (halves with each +1 cost)Strong; slow by design (72-byte password limit)
Argon2idMemory-hard KDFDepends on memory and time parameters; far lower than fast hashesRecommended default (winner of the Password Hashing Competition)

2. The Mechanics of Salting: Defeating Rainbow Tables

A cryptographic salt is a unique, cryptographically random sequence of bytes generated per password and prepended/appended prior to hashing. Salting completely destroys precomputed rainbow table attacks and prevents attackers from discovering that two users share the same password.
Anatomy of a Modular Crypt Bcrypt String
// Example Bcrypt Hash:
// $2b$12$e8Yh9TqZ8p1m3Kj7Lk9We.3oRjV6qY2N8zK1pT9sF2wE4rY6uI0oO
// [ $2b$ ] - Algorithm identifier (Bcrypt 2b format)
// [ 12$  ] - Cost factor (2^12 = 4,096 key expansion iterations)
// [ e8Yh9TqZ8p1m3Kj7Lk9We. ] - 22 chars (16 bytes Base64-encoded random salt)
// [ 3oRjV6qY2N8zK1pT9sF2wE4rY6uI0oO ] - 31 chars (184-bit Blowfish ciphertext hash)

The salt is stored directly inside the hash output string; the verification function extracts the salt automatically!

3. Bcrypt vs Argon2id: Memory Hardness

While Bcrypt is compute-intensive, it requires only 4 KB of RAM, making it susceptible to customized FPGA and ASIC hardware cracking rigs. Argon2id is a memory-hard algorithm requiring megabytes of RAM per hash evaluation, making large-scale ASIC parallelization economically unfeasible.
Production Argon2id Recommended Parameters (OWASP 2026)
/* Recommended OWASP Parameter Baseline for Argon2id:
 * Memory Cost (m) : 65,536 KiB (64 MB of RAM)
 * Time Cost (t)   : 3 iterations
 * Parallelism (p) : 4 threads
 * Target Latency  : ~500ms on server hardware
 */

import argon2 from "argon2";

// Hash password
const hash = await argon2.hash(userPassword, {
  type: argon2.argon2id,
  memoryCost: 65536,
  timeCost: 3,
  parallelism: 4
});

// Verify password
const isValid = await argon2.verify(hash, candidatePassword);

Argon2id combines Argon2d (resistance to GPU cracking) and Argon2i (resistance to side-channel cache attacks).

4. Timing Attacks & Constant-Time Comparisons

Standard string comparisons (if a === b) compare characters sequentially from left to right, terminating early on the first mismatch. Attackers can measure response latency in nanoseconds to deduce character by character. Password verifiers must always execute in constant time.
Constant-Time Equality in Node.js
import crypto from "crypto";

// ANTI-PATTERN: Leaks character matches via variable-time comparison
// if (receivedToken === storedToken) { ... }

// PRODUCTION PATTERN: Constant-time comparison immune to timing side-channels
function safeTokenCompare(a: string, b: string): boolean {
  const bufA = Buffer.from(a);
  const bufB = Buffer.from(b);
  
  if (bufA.length !== bufB.length) return false;
  return crypto.timingSafeEqual(bufA, bufB);
}

crypto.timingSafeEqual runs in deterministic execution time regardless of where string mismatches occur.

5. NIST SP 800-63B Modern Password Policy Standards

NIST (National Institute of Standards and Technology) has discarded legacy password composition rules in favor of evidence-based security.
Legacy Practice (Deprecated by NIST)Modern NIST SP 800-63B StandardRationaleCopy
Arbitrary Periodic Expiry (90 days)Do NOT force expiration without evidence of breachForces users to pick predictable patterns (Spring2026!)
Complex Character Rules (!@#$1aA)Allow minimum 12-15 characters of any UTF-8 textLength beats complexity; allow spaces in passphrases
Truncating Long PasswordsAllow passwords up to at least 64 charactersPermits password manager generated passphrases
Security Knowledge QuestionsForbidden (Mother maiden name, favorite pet)Easily researched via social engineering and OSINT

๐ŸŽ“Explore More Guides

โœจ