CWE-916: Use of Password Hash With Insufficient Computational Effort
Overview
Weak password hashing occurs when applications use fast cryptographic hash functions (MD5, SHA-1, SHA-256) or inadequately configured password hashing algorithms to store passwords. Unlike general-purpose hashing, password hashing requires intentionally slow, computationally expensive algorithms to resist brute force attacks. When attackers obtain a database dump, weak password hashes can be cracked to reveal plaintext passwords, which users often reuse across multiple sites.
OWASP Classification
A04:2025 - Cryptographic Failures
Risk
Weak password hashing creates severe security risks:
- Mass password compromise: Attackers crack passwords from stolen databases
- Credential stuffing: Cracked passwords reused on other sites (users reuse passwords)
- Account takeover: Attackers gain full access to user accounts
- Privilege escalation: Administrative passwords compromised along with regular users
- Privacy violations: Access to personal data, communications, financial information
- Compliance failures: Violates PCI-DSS, HIPAA, GDPR, SOC 2 requirements
- Reputational damage: Public disclosure of password breach
- Legal liability: Lawsuits from affected users
According to breach reports, 80%+ of passwords hashed with MD5 or SHA-1 are cracked within hours.
The Problem with Fast Hashes
Fast hash functions are vulnerable to password cracking:
- Speed: Modern GPUs can compute billions of SHA-256 hashes per second, making brute force feasible
- Rainbow tables: Pre-computed hash tables enable instant lookups for common passwords
- Brute force: Trying all possible passwords (up to certain length) becomes computationally trivial
- Dictionary attacks: Testing millions of common passwords completes in seconds
According to breach reports, 80%+ of passwords hashed with MD5 or SHA-1 are cracked within hours.
Remediation Steps
Core principle: Password hashing must use adaptive, purpose-built algorithms (Argon2id/bcrypt/scrypt) with work factors tuned to current hardware - slow enough to make brute force attacks impractical (target 250-500ms per hash) while remaining fast enough for legitimate authentication. Never use fast general-purpose hashes (MD5, SHA-1, SHA-256) for passwords.
Locate the weak password hashing in your code
- Review the flaw details to identify the specific file, line number, and code pattern
- Identify which weak hashing algorithm is in use (MD5, SHA-1, SHA-256, unsalted hash, etc.)
- Trace the password flow from user registration/login through hashing to storage
- Check database schema: review password hash column and verify if salt column exists
Replace with adaptive password hashing algorithms (Primary Defense)
Replace fast hashes with purpose-built password hashing functions:
RECOMMENDED (in priority order):
1. Argon2id
- Resistant to GPU/ASIC attacks (memory-hard and CPU-hard)
- Configuration: 19MiB memory, 2 iterations, 1 parallelism (OWASP minimum)
- Configurable: 47 MiB memory, 3-4 iterations for sensitive applications
2. bcrypt
- CPU-hard, resistant to GPUs
- Work factor: 12 (minimum), 14 (recommended for 2024)
- Automatic salt generation, max password length: 72 bytes
3. scrypt
- Good GPU resistance via memory requirements
- Configuration: N=2^17, r=8, p=1 minimum
4. PBKDF2-HMAC-SHA256
- Use ≥ 600,000 iterations (OWASP 2023 recommendation)
- 1,300,000+ for high-security applications
AVOID: MD5, SHA-1, SHA-256, SHA-512 (too fast), plain SHA-2 without key stretching, custom "salted hash" implementations, unsalted hashes
Configure adequate work factors and implement proper salting
- Tune algorithm parameters: Target 250-500ms per hash on production servers, test on actual hardware, increase work factor annually
- Configure work factors: Argon2id (19-47 MiB memory, 2-4 iterations), bcrypt (work factor 12-14), PBKDF2 (600,000+ iterations)
- Implement proper salts: Generate new random salt for EVERY password (128 bits minimum), use cryptographically secure RNG, store salt alongside hash
- Use vetted libraries: bcrypt, Argon2, scrypt handle salting automatically - no manual salt handling required
Implement migration strategy for existing password hashes
- Add version tracking: Add "hash_version" column to database to track which algorithm was used
- Implement dual-read verification: On user login, verify password against old hash (MD5/SHA-256) first, if valid immediately re-hash with new algorithm (bcrypt/Argon2)
- Update on successful login: Replace old hash with new hash and update version in database
- Monitor migration progress: Track percentage of users migrated to new algorithm
- Set migration deadline: After 90 days, force password reset for inactive accounts still using weak hashes
- Remove legacy support: Once 95%+ migrated, stop accepting old algorithm hashes
Monitor and audit password hashing usage
- Log password hashing operations for security monitoring (registration, login, password change)
- Alert on usage of deprecated algorithms if dual-read is still active
- Track migration progress with database queries (SELECT hash_version, COUNT(*) FROM users GROUP BY hash_version)
- Review code for remaining instances of weak hashing (grep for MD5, SHA256, etc.)
- Monitor hash computation time to ensure work factor is adequate
Test the password hashing changes thoroughly
- Test migration in staging with production data copy
- Verify user login works with both old and new hashes during migration
- Test password registration creates new hashes with proper algorithm
- Verify hash computation time meets 250-500ms target
- Test rollback procedures if migration causes issues
- Re-scan with security scanner to confirm the issue is resolved
Migration Considerations
CRITICAL: Changing password hashing algorithms will prevent users from logging in with their existing passwords unless you implement a migration strategy.
What Breaks
- Existing password hashes become invalid: Users cannot authenticate with their current passwords
- All login attempts fail: Changing from MD5/SHA-256 to bcrypt means old hashes won't match new algorithm
- User lockout: Without migration, users must reset passwords via email recovery
- Support burden: Influx of "can't log in" tickets if not handled properly
- Business impact: E-commerce checkout abandonment, SaaS service interruption, customer frustration
Migration Approach
Dual-Read Strategy (Strongly Recommended)
Gradually upgrade passwords as users log in:
- Add algorithm version tracking: Store which algorithm was used for each password hash
- Implement dual verification: Check password against both old (MD5/SHA-256) and new (bcrypt/Argon2) algorithms
- Upgrade on successful login: When user logs in with old hash successfully, immediately re-hash with new algorithm and update database
- Monitor migration progress: Track percentage of users migrated to new algorithm
- Set migration deadline: After 90 days, force password reset for inactive accounts still using weak hashes
- Remove legacy support: Once 95%+ migrated, stop accepting old algorithm hashes
Big-Bang Migration (NOT Recommended)
Force all users to reset passwords:
- Invalidate all existing password hashes
- Send password reset emails to entire user base
- Mark all accounts as requiring password reset
- Impact: All users locked out until they reset via email. High support burden, poor user experience.
Rollback Procedures
If migration causes issues:
- Code rollback: Revert application to previous version that supports old algorithm
- Database rollback: Restore from backup if hashes were modified
- Emergency access: Temporarily re-enable dual-read if old algorithm support was removed too early
-
Communication plan:
- Status page: "Investigating login issues"
- User email: "Temporary login problems resolved"
- Support team: "Use password reset for affected users"
Testing Recommendations
Pre-deployment testing:
- Test migration in staging with production data copy
- Verify old passwords still work (dual-read)
- Verify old passwords upgrade after successful login
- Verify new passwords work immediately
- Test failed login attempts don't break migration
- Verify migration tracking returns correct percentages
- Test password reset flow for un-migrated users
Post-deployment monitoring:
- Monitor login success/failure rates (should remain constant)
- Track migration progress (% of users upgraded)
- Alert on authentication error rate increase
- Monitor support tickets for login issues
- Track time-to-migrate (estimate completion)
Key metrics to track:
- Total users in database
- Users on new algorithm vs. old algorithm
- Daily migration rate
- Authentication error rates
- Projected completion date
Testing and Verification
Verify Strong Algorithm
# Test that bcrypt/Argon2 is actually used
from bcrypt import hashpw, gensalt
# Hash should start with $2b$ (bcrypt) or $argon2id$ (Argon2)
password = b"testpassword"
hash = hashpw(password, gensalt(rounds=12))
print(hash) # Should show: b'$2b$12$...'
Measure Hash Time
# Hash operation should take 250-500ms
time measure_hash_duration()
# Expected: 250ms-500ms per hash
# Too fast (< 100ms): Increase work factor
# Too slow (> 1s): Decrease work factor or use faster hardware
Test Salt Uniqueness
Create multiple hashes of same password:
hash1 = hash("password123")
hash2 = hash("password123")
# Hashes should be different (different salts)
assert hash1 != hash2
# But both should verify correctly
assert verify("password123", hash1) == True
assert verify("password123", hash2) == True
Code Review Verification
# Search for weak hashing algorithms
grep -r "MD5\|SHA1\|SHA-256" --include="*.java" --include="*.py" --include="*.cs"
# Verify password-specific hashing is used
grep -r "bcrypt\|argon2\|scrypt\|PBKDF2" --include="*.java" --include="*.py"
Expected result: No fast hashes for passwords; only bcrypt/Argon2/scrypt/PBKDF2.
Database Inspection
-- Check stored password hashes
SELECT LEFT(password_hash, 10), COUNT(*)
FROM users
GROUP BY LEFT(password_hash, 10);
-- Expected patterns:
-- $2b$12$... (bcrypt)
-- $argon2id$ (Argon2)
-- pbkdf2_sha256$ (Django PBKDF2)
-- Should NOT see:
-- 5f4dcc3b5a (MD5 - 32 hex chars)
-- 5baa61e4c9 (SHA-1 - 40 hex chars)
Automated Security Testing
- Use password audit tools (hashcat, john the ripper) against test hashes
- Verify weak hashes are cracked quickly, strong hashes resist cracking
- Include in security test suite