weak-password-hashing-anti-pattern
Weak Password Hashing Anti-Pattern
Severity: High
Summary
Applications use fast general-purpose hash functions (MD5, SHA-1, SHA-256) without salting for password storage, enabling rapid cracking via rainbow tables or GPU-accelerated brute-force (billions of hashes per second). Results in mass account compromise and credential stuffing attacks.
The Anti-Pattern
The anti-pattern is using cryptographic hash functions that are too fast or lack essential features like salting and adjustable work factors, making them vulnerable to offline attacks.
BAD Code Example
# VULNERABLE: Using MD5 for password hashing.
import hashlib
def hash_password_md5(password):
# MD5 is a cryptographically broken hash function.
# It is extremely fast, and rainbow tables for MD5 are widely available.
return hashlib.md5(password.encode()).hexdigest()
def verify_password_md5(password, stored_hash):
return hash_password_md5(password) == stored_hash
# Another example: plain SHA-256 without salting.
def hash_password_sha256_unsalted(password):
# SHA-256 is a strong hash for data integrity, but too fast for passwords.
# Without a salt, identical passwords result in identical hashes.
return hashlib.sha256(password.encode()).hexdigest()
# Problems:
# - Speed: MD5/SHA-256 can compute billions of hashes per second.
# - No Salt: Allows rainbow table attacks and reveals users with identical passwords.
# - No Work Factor: Cannot be slowed down to resist brute-force attacks.
GOOD Code Example
# SECURE: Use a password-hashing algorithm designed to be slow and include a unique salt.
import bcrypt # Or Argon2, scrypt
def hash_password_secure(password):
# bcrypt generates unique salt per password and supports adjustable work factor.
# Higher rounds = slower hashing = better brute-force resistance.
hashed_password = bcrypt.hashpw(password.encode('utf-8'), bcrypt.gensalt(rounds=12))
return hashed_password.decode('utf-8') # Store the hashed password as a string.
def verify_password_secure(password, stored_hash):
# checkpw() verifies password against stored hash with constant-time comparison.
# Extracts salt and work factor from stored hash to prevent timing attacks.
return bcrypt.checkpw(password.encode('utf-8'), stored_hash.encode('utf-8'))
# Recommended algorithms (in order of current preference):
# 1. Argon2id (best practice for new applications)
# 2. bcrypt
# 3. scrypt
# Always use libraries for password hashing; never implement your own.
Detection
- Code Review: Search your codebase for password hashing implementations.
- Look for
hashlib.md5(),hashlib.sha1(), orhashlib.sha256()being used for passwords. - Check if
bcrypt,argon2, orscryptlibraries are used. - Verify that a unique, cryptographically secure salt is generated for each password.
- Look for
- Database Inspection: Look at the
passwordorpassword_hashcolumn in your user database.- Are the hashes all of the same length and format? (Suggests no salt or static salt).
- Do they start with prefixes like
$2a$(bcrypt),$argon2id$(Argon2), or$s2$(scrypt)?
- Check for plaintext passwords: Ensure that passwords are never stored in plaintext.
Prevention
- Use strong, slow, adaptive password-hashing functions.
- Argon2id: Currently the recommended algorithm for new applications.
- bcrypt: A widely used and strong algorithm.
- scrypt: Another strong algorithm.
- Always use a unique, cryptographically secure salt for each password. bcrypt and Argon2 generate salts automatically.
- Adjust the work factor (cost) appropriately. Increase the number of rounds (bcrypt) or memory/time cost (Argon2) until hashing takes about 250-500 milliseconds on your server hardware. This makes brute-forcing expensive for attackers.
- Never use fast, general-purpose hash functions like MD5, SHA-1, or plain SHA-256 for passwords. These are designed for speed, not for password storage.
- Never store plaintext passwords.
Related Security Patterns & Anti-Patterns
- Hardcoded Secrets Anti-Pattern: Password hashes are sensitive data and should be protected.
- Missing Authentication Anti-Pattern: Weak password hashing undermines the entire authentication process.
- Timing Attacks Anti-Pattern: Proper password-hashing libraries use constant-time comparisons to prevent timing attacks during verification.
References
- OWASP Top 10 A04:2025 - Cryptographic Failures
- OWASP GenAI LLM10:2025 - Unbounded Consumption
- OWASP API Security API2:2023 - Broken Authentication
- OWASP Password Storage Cheat Sheet
- CWE-327: Use of a Broken or Risky Cryptographic Algorithm
- CWE-759: Use of a One-Way Hash without a Salt
- CAPEC-55: Rainbow Table Password Cracking
- PortSwigger: Authentication
- BlueKrypt - Cryptographic Key Length Recommendation
- Source: sec-context
More from igbuend/grimbard
tikz
LaTeX TikZ/PGF package for programmatic vector graphics and diagrams. Use when helping users draw flowcharts, trees, graphs, automata, circuits, geometric figures, or any custom diagram in LaTeX.
95latex
Comprehensive LaTeX reference for document creation, formatting, mathematics, tables, figures, bibliographies, and compilation. Use when helping users write, edit, debug, or compile LaTeX documents.
38pgfplots
LaTeX pgfplots package for data visualization and plotting. Use when helping users create line plots, bar charts, scatter plots, histograms, 3D surfaces, or any scientific/data plot in LaTeX.
34biblatex
LaTeX biblatex/biber packages for modern bibliography management. Use when helping users cite references, manage .bib files, choose citation styles, or troubleshoot bibliography compilation.
24ethical-hacking-ethics
Legal and ethical guidelines for bug bounties, pentesting, and security research. Use when conducting authorized security testing.
13amsmath
LaTeX amsmath/amssymb/mathtools packages for mathematical typesetting. Use when helping users write equations, align math, use mathematical symbols, matrices, theorems, or any advanced math formatting.
12