Back to list
igbuend

weak-password-hashing-anti-pattern

by igbuend

A repository of security related skills - like secure code review and pentesting - for Claude and other AI.

2🍴 1📅 Jan 24, 2026

SKILL.md


name: "weak-password-hashing-anti-pattern" description: "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."

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.

References

Score

Total Score

70/100

Based on repository quality metrics

SKILL.md

SKILL.mdファイルが含まれている

+20
LICENSE

ライセンスが設定されている

+10
説明文

100文字以上の説明がある

+10
人気

GitHub Stars 100以上

0/15
最近の活動

3ヶ月以内に更新がある

0/10
フォーク

10回以上フォークされている

0/5
Issue管理

オープンIssueが50未満

+5
言語

プログラミング言語が設定されている

+5
タグ

1つ以上のタグが設定されている

0/5

Reviews

💬

Reviews coming soon