Weakness reference
CWE-916

Use of Password Hash With Insufficient Computational Effort

This weakness occurs when a password hashing algorithm is too fast or simple, allowing attackers who obtain a stolen password database to crack hashes offline…

01Summary

This weakness occurs when a password hashing algorithm is too fast or simple, allowing attackers who obtain a stolen password database to crack hashes offline through brute-force or dictionary attacks. Modern password hashing should be intentionally slow and resource-intensive to make such attacks impractical. Using weak algorithms like MD5, SHA-1, or unsalted hashes puts user accounts at serious risk even if the database itself is encrypted.

02How It Happens

Password hashing algorithms vary dramatically in computational cost. General-purpose cryptographic hashes (MD5, SHA-1, SHA-256) are designed for speed and are unsuitable for passwords. When an attacker obtains a stolen password database, they can test millions or billions of candidate passwords per second against fast hashes on commodity hardware. Proper password hashing uses algorithms like bcrypt, scrypt, Argon2, or PBKDF2 with high iteration counts, which deliberately consume CPU time and memory, making offline cracking prohibitively expensive. Omitting salt, using a single global salt, or choosing iteration counts that are too low all fall into this category.

03Real-World Impact

If a password database is breached, weak hashes can be cracked in hours or days, compromising every user account. Attackers gain access to user data, can pivot to other services where users reused passwords, and may impersonate legitimate users to perform fraud or further attacks. The damage is often invisible until accounts are actively abused, making detection and response difficult. Organizations may face regulatory penalties, loss of user trust, and liability for inadequate security controls.

04Vulnerable & Fixed Patterns

Vulnerable pattern
import hashlib

def hash_password(password):
    return hashlib.sha256(password.encode()).hexdigest()

def verify_password(password, stored_hash):
    return hashlib.sha256(password.encode()).hexdigest() == stored_hash

Why it's vulnerable:
SHA-256 is a fast general-purpose hash designed for data integrity, not password storage. An attacker with the hash can test billions of guesses per second using GPU or ASIC hardware.

Fixed pattern
import bcrypt

def hash_password(password):
    salt = bcrypt.gensalt(rounds=12)
    return bcrypt.hashpw(password.encode(), salt)

def verify_password(password, stored_hash):
    return bcrypt.checkpw(password.encode(), stored_hash)
Vulnerable pattern
<?php
function hash_password($password) {
    return md5($password);
}

function verify_password($password, $stored_hash) {
    return md5($password) === $stored_hash;
}
?>

Why it's vulnerable:
MD5 is cryptographically broken and extremely fast. It provides no salt and no computational cost, making it trivial to crack with precomputed rainbow tables or brute force.

Fixed pattern
<?php
function hash_password($password) {
    return password_hash($password, PASSWORD_BCRYPT, ['cost' => 12]);
}

function verify_password($password, $stored_hash) {
    return password_verify($password, $stored_hash);
}
?>

05Prevention Checklist

Use a dedicated password hashing algorithm: bcrypt, scrypt, Argon2, or PBKDF2 with at least 100,000 iterations.
Never use general-purpose hashes (MD5, SHA-1, SHA-256) for passwords, even with salt.
Ensure each password is hashed with a unique, randomly generated salt (most modern libraries handle this automatically).
Set computational cost parameters (work factor, rounds, memory) high enough that a single hash takes 100–500 milliseconds on your server hardware.
Audit existing password storage: identify any use of weak algorithms and plan a migration strategy (e.g., rehash on next login).
Use a password manager or identity provider to avoid storing passwords directly whenever possible.

06Signs You May Already Be Affected

Review your authentication code for calls to md5(), sha1(), sha256(), or crypt() without a modern algorithm wrapper. Check your database schema for password columns that are suspiciously short (e.g., 32 characters for MD5) or lack a salt column. If a password breach has occurred, monitor for unusual login attempts or account takeovers, which may indicate successful offline cracking.

07Related Recent Vulnerabilities