Password Security: Hashing, Salting & Bcrypt vs Argon2 Guide

SCR Team
February 15, 2026
14 min read
532 words
Share

Password Security: Hashing & Salting — Bcrypt vs Argon2 (2025)

Passwords are still the #1 authentication method. LinkedIn (6M passwords leaked), Yahoo (3B accounts), Equifax (147M identities) — inadequate hashing played a major role in each disaster.

This guide teaches proper password storage that stops attackers cold.

Why Password Hashing Matters

Plaintext passwords stolen? Instant full access to every user account.
Fast hashes (MD5, SHA1)? Modern GPUs crack billions per second using rainbow tables.
Unsalted hashes? Attacker uses pre-computed tables and bypasses your hash entirely.
Weak hashing algorithm? All your stored passwords are effectively encrypted with a weak cipher.


Algorithm Comparison (2025)

AlgorithmSpeedMemoryGPU-ResistantRecommendedBest For
Plaintext⚡⚡⚡ InstantNone❌ NoNever
MD5/SHA1⚡⚡ FastNone❌ NoNever
SHA256⚡ FastNone❌ NoAvoid
PBKDF2🚷 SlowLow⚠️ Medium✅ FIPS systemsLegacy compliance
bcrypt🚷 SlowLow✅ GoodYesMost applications
scrypt🚷🚷 SlowerHigh✅✅ ExcellentYesHigh-security apps
Argon2id🚷🚷 SlowerHigh✅✅ Excellent✅✅ BestNew projects (NIST)

Bcrypt: The Battle-Tested Choice

Bcrypt was designed in 1999 and is still safe TODAY because it intentionally uses exponential slowdown (cost factor). The slower hashing is a feature, not a bug.

How Bcrypt Works

  1. Generate a random salt from 2^cost rounds of permutation
  2. Run password + salt through Blowfish cipher cost times
  3. Store the salt + cost + hash together

Example:

  • Input password: "hunter2"
  • Cost: 12 (2^12 = 4,096 iterations)
  • Result: $2b$12$aWjd2hgh3vWs4Ejz8jK47DpO3U5/s8Zi5hj5H4M7HpI...
  • Time to hash: ~250ms (intentionally slow)

Node.js Bcrypt Implementation

Install:

npm install bcryptjs

Hash password:

const bcrypt = require('bcryptjs');

async function registerUser(email, password) {
  const salt = await bcrypt.genSalt(12);  // Cost factor 12
  const hashedPassword = await bcrypt.hash(password, salt);
  
  // Store hashedPassword in database
  await User.create({ email, passwordHash: hashedPassword });
}

Verify password:

async function loginUser(email, passwordAttempt) {
  const user = await User.findOne({ email });
  const isValid = await bcrypt.compare(passwordAttempt, user.passwordHash);
  
  if (isValid) {
    // Authentication successful
    return createSession(user);
  } else {
    // Wrong password
    return 'Invalid credentials';
  }
}

Cost factor timing:

  • Cost 10: 10ms (old hardware)
  • Cost 12: 250ms (standard, 2024)
  • Cost 14: 1 second (future-proofing)
  • Cost 16: 2 seconds (admin accounts only)

Argon2: The Modern Standard

Argon2 won the Password Hashing Competition (2015) and is now recommended by NIST. It's memory-hard, making GPU/ASIC attacks infeasible.

Node.js Argon2 Implementation

Install:

npm install argon2

Hash:

const argon2 = require('argon2');

async function registerUserArgon2(email, password) {
  const hash = await argon2.hash(password, {
    type: argon2.argon2id,  // Recommended
    memoryCost: 65536,      // 64 MB
    timeCost: 3,            // 3 iterations
    parallelism: 4          // 4 parallel threads
  });
  
  await User.create({ email, passwordHash: hash });
}

Verify:

async function loginUserArgon2(email, passwordAttempt) {
  const user = await User.findOne({ email });
  const isValid = await argon2.verify(user.passwordHash, passwordAttempt);
  
  if (isValid) {
    return createSession(user);
  } else {
    return 'Invalid credentials';
  }
}

Python Argon2

pip install argon2-cffi
from argon2 import PasswordHasher

hasher = PasswordHasher()
hash = hasher.hash('hunter2')
hasher.verify(hash, 'hunter2')  # True

Beyond Hashing: Defense in Depth

Salts & Peppers:

  • Salt: Random per-password, stored with hash (bcrypt/argon2 handle this)
  • Pepper: Server secret appended before hashing (stored in env, NOT in code)

Password Rotation:

  • Re-hash on next login when you upgrade algorithms
  • Support graceful migration from old to new algorithms

Additional Protections:

  • Block common passwords (rockyou.txt, have-i-been-pwned lists)
  • Enforce minimum length (12+ characters)
  • Rate limit login attempts (exponential backoff)
  • Monitor for credential stuffing (impossible logins from new IPs)
  • Require MFA (passwords alone insufficient)

Migration Plan: Upgrading Hash Algorithms

If you're using MD5/SHA1:

// On login, check if password matches old hash
if (md5(password) === user.oldMd5Hash) {
  // Authentication passed! Re-hash with bcrypt
  user.bcryptHash = await bcrypt.hash(password, 12);
  user.oldMd5Hash = null;
  await user.save();
}

Password Security Checklist

  • Use Argon2id (new projects) or bcrypt (existing)
  • Cost/time parameters tuned to 250-500ms per hash
  • Never use MD5, SHA1, or unsalted hashes
  • Block common passwords (NIST list)
  • Enforce minimum 12-character passwords
  • Rate limit login endpoints (exponential backoff)
  • Require MFA for sensitive operations
  • Monitor for credential stuffing
  • Support password rotation/upgrade on next login

Resources

Next Step: Audit your password storage now. If using MD5/SHA1, implement bcrypt upgrade on next login.

Editorial standards

Published by SecureCodeReviews

This article is part of our original AI security and cybersecurity content library. We show publish and update dates, keep company and policy pages public, and update important guidance when material changes affect readers.

Named author: SCR Team
Published: Feb 15, 2026
Update status: current publication version

Questions or corrections?

Review our editorial standards, learn more about the company, or contact us if a page needs clarification.

Secure Code Review

Want an expert review before this issue reaches production?

We combine manual code review with AppSec tooling to find vulnerabilities, logic flaws, and insecure patterns before release or audit deadlines.

Manual secure code review for real exploitable issues
Remediation guidance with clear engineering next steps
Useful for launch reviews, client audits, and security hardening

Talk to SecureCodeReviews

Get a scoped review path fast

Manual review
Actionable fixes
Fast turnaround
Security-focused

Advertisement