weak-password-hashing-anti-pattern

Security anti-pattern for weak password hashing (CWE-327, CWE-759). Use when generating or reviewing code that stores or verifies user passwords. Detects use of MD5, SHA1, SHA256 without salt, or missing password hashing entirely. Recommends bcrypt, Argon2, or scrypt.

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "weak-password-hashing-anti-pattern" with this command: npx skills add igbuend/grimbard/igbuend-grimbard-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(), or hashlib.sha256() being used for passwords.
    • Check if bcrypt, argon2, or scrypt libraries are used.
    • Verify that a unique, cryptographically secure salt is generated for each password.
  • Database Inspection: Look at the password or password_hash column 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

References

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Security

missing-security-headers-anti-pattern

No summary provided by upstream source.

Repository SourceNeeds Review
Security

oauth-security-anti-pattern

No summary provided by upstream source.

Repository SourceNeeds Review
Security

content-security-policy

No summary provided by upstream source.

Repository SourceNeeds Review
General

tikz

No summary provided by upstream source.

Repository SourceNeeds Review