flawopen.com/Vulnerabilities/Insecure Password Hashing

Insecure Password Hashing & Salt Reuse

High Severity CWE-916 Cryptography & Auth
ELI5 — The Speed Trap for Bank Robbers

Imagine building a bank vault door. A standard lock (SHA-256) is designed for computers to open in one nanosecond. A modern supercomputer can test 100 billion keys every second on a cheap gaming graphics card (GPU). If hackers steal your password database, they crack every password before lunchtime. A password hashing lock (Argon2id) is intentionally heavy and slow—it forces the computer to spend half a second and 64 megabytes of memory on every single guess, making brute-force attacks mathematically impossible.

Target: User password databases, credential storage
Vector: GPU / ASIC brute-force attacks, precomputed rainbow tables
Impact: Mass credential stuffing, credential recovery after database leak
Remediation: Memory-hard password KDFs: Argon2id, bcrypt, unique per-user salts

The Mechanism & Root Cause

Algorithms like MD5, SHA-1, and SHA-256 are general-purpose cryptographic hashes designed to be calculated as fast as possible (for file checksums). Modern GPUs calculate billions of SHA-256 hashes per second. Storing passwords using fast hashes—even with a salt—leaves them vulnerable to offline GPU cracking.

user_auth.py (Vulnerable SHA-256)Vulnerable
# VULNERABLE: Fast SHA-256 is easily cracked on consumer GPUs
import hashlib

def hash_password(password, salt):
    # A single modern Nvidia RTX 4090 tests >20,000,000,000 SHA-256 hashes/sec!
    # Even salted, dictionary passwords are recovered in minutes
    return hashlib.sha256((salt + password).encode()).hexdigest()
user_auth.py (Hardened Argon2id)Hardened
# HARDENED: Argon2id (OWASP recommended) enforces memory-hard work factors
from argon2 import PasswordHasher

# Configured for memory hardness: 64MB memory, 3 iterations, 4 parallel threads
ph = PasswordHasher(time_cost=3, memory_cost=65536, parallelism=4)

def hash_password_safe(password):
    # Automatically generates unique cryptographically secure salt and embed params
    return ph.hash(password)

def verify_password_safe(stored_hash, candidate_password):
    return ph.verify(stored_hash, candidate_password)

The Attack & Exploit Sequence

Defensive Engineering & Prevention Rules

Explore related security topics and post-mortems: Complete Security Directory →