Weak Password Hashing Algorithms and Practices

High Risk Cryptographic Security
password-hashingauthenticationbcryptscryptargon2pbkdf2saltingrainbow-tablesbrute-forcepassword-storage

What it is

A critical vulnerability where applications use weak password hashing algorithms (MD5, SHA-1, SHA-256 without salt) or implement password hashing incorrectly. This makes stored passwords vulnerable to rainbow table attacks, brute force attacks, and password recovery through cryptographic weaknesses.

# Python - VULNERABLE: Multiple weak password practices
import hashlib
import sqlite3
import time
from datetime import datetime

class VulnerableAuthSystem:
    def __init__(self, db_path="users.db"):
        self.conn = sqlite3.connect(db_path)
        self.create_tables()
    
    def create_tables(self):
        cursor = self.conn.cursor()
        cursor.execute("""
            CREATE TABLE IF NOT EXISTS users (
                id INTEGER PRIMARY KEY,
                username TEXT UNIQUE,
                password_hash TEXT,
                created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
            )
        """)
        self.conn.commit()
    
    def hash_password_md5(self, password):
        """VULNERABLE: Using MD5 for password hashing"""
        return hashlib.md5(password.encode()).hexdigest()
    
    def hash_password_sha256_no_salt(self, password):
        """VULNERABLE: SHA-256 without salt"""
        return hashlib.sha256(password.encode()).hexdigest()
    
    def hash_password_weak_salt(self, password, username):
        """VULNERABLE: Predictable salt based on username"""
        salt = username.lower()  # Predictable salt!
        salted_password = password + salt
        return hashlib.sha256(salted_password.encode()).hexdigest()
    
    def register_user(self, username, password):
        """VULNERABLE: No password policy enforcement"""
        cursor = self.conn.cursor()
        
        # No password strength validation
        if len(password) < 3:  # Extremely weak requirement
            return False, "Password too short"
        
        # Using weak hashing method
        password_hash = self.hash_password_weak_salt(password, username)
        
        try:
            cursor.execute(
                "INSERT INTO users (username, password_hash) VALUES (?, ?)",
                (username, password_hash)
            )
            self.conn.commit()
            return True, "User registered successfully"
        except sqlite3.IntegrityError:
            return False, "Username already exists"
    
    def login_user(self, username, password):
        """VULNERABLE: Multiple authentication issues"""
        cursor = self.conn.cursor()
        
        cursor.execute(
            "SELECT password_hash FROM users WHERE username = ?",
            (username,)
        )
        
        result = cursor.fetchone()
        if not result:
            return False, "User not found"
        
        stored_hash = result[0]
        
        # VULNERABLE: Try multiple hash methods (revealing hash type)
        computed_hash_md5 = self.hash_password_md5(password)
        computed_hash_sha256 = self.hash_password_sha256_no_salt(password)
        computed_hash_weak_salt = self.hash_password_weak_salt(password, username)
        
        # VULNERABLE: Timing attack possible
        if (stored_hash == computed_hash_md5 or 
            stored_hash == computed_hash_sha256 or 
            stored_hash == computed_hash_weak_salt):
            return True, "Login successful"
        
        return False, "Invalid password"
    
    def change_password(self, username, old_password, new_password):
        """VULNERABLE: No verification of old password strength"""
        # Verify old password first
        login_success, _ = self.login_user(username, old_password)
        if not login_success:
            return False, "Current password incorrect"
        
        # No validation of new password strength
        if len(new_password) < 3:
            return False, "New password too short"
        
        # Update with same weak hashing
        new_hash = self.hash_password_weak_salt(new_password, username)
        
        cursor = self.conn.cursor()
        cursor.execute(
            "UPDATE users SET password_hash = ? WHERE username = ?",
            (new_hash, username)
        )
        self.conn.commit()
        
        return True, "Password changed successfully"
    
    def reset_password(self, username):
        """VULNERABLE: Weak password reset"""
        # Generate weak temporary password
        import random
        temp_password = str(random.randint(100000, 999999))  # 6-digit number
        
        # Hash with weak method
        temp_hash = self.hash_password_weak_salt(temp_password, username)
        
        cursor = self.conn.cursor()
        cursor.execute(
            "UPDATE users SET password_hash = ? WHERE username = ?",
            (temp_hash, username)
        )
        self.conn.commit()
        
        return temp_password  # DANGEROUS: Returning plaintext password
    
    def get_user_passwords(self):
        """VULNERABLE: Exposing password hashes"""
        cursor = self.conn.cursor()
        cursor.execute("SELECT username, password_hash FROM users")
        return cursor.fetchall()
    
    def bulk_password_check(self, password_list):
        """VULNERABLE: Allows bulk password testing"""
        results = []
        
        for password in password_list:
            for username, stored_hash in self.get_user_passwords():
                computed_hash = self.hash_password_weak_salt(password, username)
                if computed_hash == stored_hash:
                    results.append((username, password))
        
        return results
# Python - SECURE: Strong password security implementation
import bcrypt
import secrets
import sqlite3
import time
import re
import hashlib
import hmac
from datetime import datetime, timedelta
from typing import Tuple, Optional, List
import logging

class SecureAuthSystem:
    def __init__(self, db_path="secure_users.db"):
        self.conn = sqlite3.connect(db_path, check_same_thread=False)
        self.create_tables()
        self.bcrypt_rounds = 14  # Secure work factor
        self.max_login_attempts = 5
        self.lockout_duration = timedelta(minutes=15)
        self.password_history_count = 5
        
        # Password policy
        self.min_password_length = 12
        self.max_password_length = 128
        
        # Common passwords (in production, load from file)
        self.common_passwords = self._load_common_passwords()
        
        # Setup logging
        logging.basicConfig(level=logging.INFO)
        self.logger = logging.getLogger(__name__)
    
    def create_tables(self):
        cursor = self.conn.cursor()
        
        # Users table with security fields
        cursor.execute("""
            CREATE TABLE IF NOT EXISTS users (
                id INTEGER PRIMARY KEY,
                username TEXT UNIQUE NOT NULL,
                password_hash TEXT NOT NULL,
                password_created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
                failed_login_attempts INTEGER DEFAULT 0,
                locked_until TIMESTAMP NULL,
                requires_password_change BOOLEAN DEFAULT FALSE,
                created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
                updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
            )
        """)
        
        # Password history table
        cursor.execute("""
            CREATE TABLE IF NOT EXISTS password_history (
                id INTEGER PRIMARY KEY,
                user_id INTEGER,
                password_hash TEXT NOT NULL,
                created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
                FOREIGN KEY (user_id) REFERENCES users (id)
            )
        """)
        
        # Login attempts table for monitoring
        cursor.execute("""
            CREATE TABLE IF NOT EXISTS login_attempts (
                id INTEGER PRIMARY KEY,
                username TEXT,
                ip_address TEXT,
                success BOOLEAN,
                attempted_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
            )
        """)
        
        self.conn.commit()
    
    def _load_common_passwords(self) -> set:
        """Load common passwords list"""
        common = {
            'password', '123456', 'password123', 'admin', 'qwerty',
            'letmein', 'welcome', 'monkey', '1234567890', 'abc123',
            '12345678', 'princess', 'dragon', 'sunshine', 'iloveyou'
        }
        return common
    
    def validate_password_strength(self, password: str, username: str = None) -> Tuple[bool, str]:
        """Comprehensive password strength validation"""
        if len(password) < self.min_password_length:
            return False, f"Password must be at least {self.min_password_length} characters long"
        
        if len(password) > self.max_password_length:
            return False, f"Password must be no more than {self.max_password_length} characters long"
        
        # Check character requirements
        if not re.search(r'[A-Z]', password):
            return False, "Password must contain at least one uppercase letter"
        
        if not re.search(r'[a-z]', password):
            return False, "Password must contain at least one lowercase letter"
        
        if not re.search(r'\d', password):
            return False, "Password must contain at least one number"
        
        if not re.search(r'[!@#$%^&*()_+\-=\[\]{}|;:,.<>?]', password):
            return False, "Password must contain at least one special character"
        
        # Check for username in password
        if username and username.lower() in password.lower():
            return False, "Password cannot contain username"
        
        # Check against common passwords
        if password.lower() in self.common_passwords:
            return False, "Password is too common"
        
        # Check for repeated characters
        if self._has_repeated_patterns(password):
            return False, "Password contains too many repeated patterns"
        
        # Check for sequential characters
        if self._has_sequential_patterns(password):
            return False, "Password contains sequential patterns"
        
        return True, "Password meets security requirements"
    
    def _has_repeated_patterns(self, password: str) -> bool:
        """Check for repeated character patterns"""
        # Check for 3+ consecutive identical characters
        for i in range(len(password) - 2):
            if password[i] == password[i+1] == password[i+2]:
                return True
        return False
    
    def _has_sequential_patterns(self, password: str) -> bool:
        """Check for sequential character patterns"""
        sequences = ['abc', '123', 'qwe', 'asd', 'zxc']
        password_lower = password.lower()
        
        for seq in sequences:
            if seq in password_lower or seq[::-1] in password_lower:
                return True
        
        return False
    
    def hash_password(self, password: str) -> str:
        """Securely hash password using bcrypt"""
        password_bytes = password.encode('utf-8')
        salt = bcrypt.gensalt(rounds=self.bcrypt_rounds)
        hashed = bcrypt.hashpw(password_bytes, salt)
        return hashed.decode('utf-8')
    
    def verify_password(self, password: str, hashed_password: str) -> bool:
        """Verify password against hash using timing-safe comparison"""
        try:
            password_bytes = password.encode('utf-8')
            hashed_bytes = hashed_password.encode('utf-8')
            return bcrypt.checkpw(password_bytes, hashed_bytes)
        except (ValueError, TypeError):
            return False
    
    def is_account_locked(self, username: str) -> bool:
        """Check if account is locked"""
        cursor = self.conn.cursor()
        cursor.execute(
            "SELECT locked_until FROM users WHERE username = ?",
            (username,)
        )
        
        result = cursor.fetchone()
        if not result or not result[0]:
            return False
        
        locked_until = datetime.fromisoformat(result[0])
        return datetime.now() < locked_until
    
    def record_login_attempt(self, username: str, success: bool, ip_address: str = None):
        """Record login attempt for monitoring"""
        cursor = self.conn.cursor()
        
        # Record in login attempts table
        cursor.execute(
            "INSERT INTO login_attempts (username, ip_address, success) VALUES (?, ?, ?)",
            (username, ip_address, success)
        )
        
        if not success:
            # Increment failed attempts
            cursor.execute(
                "UPDATE users SET failed_login_attempts = failed_login_attempts + 1 WHERE username = ?",
                (username,)
            )
            
            # Check if we should lock the account
            cursor.execute(
                "SELECT failed_login_attempts FROM users WHERE username = ?",
                (username,)
            )
            
            result = cursor.fetchone()
            if result and result[0] >= self.max_login_attempts:
                locked_until = datetime.now() + self.lockout_duration
                cursor.execute(
                    "UPDATE users SET locked_until = ? WHERE username = ?",
                    (locked_until.isoformat(), username)
                )
                
                self.logger.warning(f"Account {username} locked due to {result[0]} failed attempts")
        else:
            # Reset failed attempts on successful login
            cursor.execute(
                "UPDATE users SET failed_login_attempts = 0, locked_until = NULL WHERE username = ?",
                (username,)
            )
        
        self.conn.commit()
    
    def check_password_history(self, user_id: int, new_password: str) -> bool:
        """Check if password was used recently"""
        cursor = self.conn.cursor()
        cursor.execute(
            """SELECT password_hash FROM password_history 
               WHERE user_id = ? 
               ORDER BY created_at DESC 
               LIMIT ?""",
            (user_id, self.password_history_count)
        )
        
        for (old_hash,) in cursor.fetchall():
            if self.verify_password(new_password, old_hash):
                return False  # Password was used before
        
        return True  # Password is new
    
    def add_to_password_history(self, user_id: int, password_hash: str):
        """Add password to history and clean old entries"""
        cursor = self.conn.cursor()
        
        # Add new password to history
        cursor.execute(
            "INSERT INTO password_history (user_id, password_hash) VALUES (?, ?)",
            (user_id, password_hash)
        )
        
        # Clean old password history entries
        cursor.execute(
            """DELETE FROM password_history WHERE user_id = ? AND id NOT IN (
                 SELECT id FROM password_history WHERE user_id = ? 
                 ORDER BY created_at DESC LIMIT ?
               )""",
            (user_id, user_id, self.password_history_count)
        )
        
        self.conn.commit()
    
    def register_user(self, username: str, password: str) -> Tuple[bool, str]:
        """Register new user with comprehensive security checks"""
        # Validate username
        if not username or len(username) < 3:
            return False, "Username must be at least 3 characters long"
        
        if not re.match(r'^[a-zA-Z0-9_]+$', username):
            return False, "Username can only contain letters, numbers, and underscores"
        
        # Validate password strength
        is_valid, message = self.validate_password_strength(password, username)
        if not is_valid:
            return False, message
        
        # Hash password
        password_hash = self.hash_password(password)
        
        cursor = self.conn.cursor()
        try:
            cursor.execute(
                "INSERT INTO users (username, password_hash) VALUES (?, ?)",
                (username, password_hash)
            )
            
            user_id = cursor.lastrowid
            
            # Add to password history
            self.add_to_password_history(user_id, password_hash)
            
            self.conn.commit()
            
            self.logger.info(f"User {username} registered successfully")
            return True, "User registered successfully"
            
        except sqlite3.IntegrityError:
            return False, "Username already exists"
    
    def login_user(self, username: str, password: str, ip_address: str = None) -> Tuple[bool, str]:
        """Authenticate user with comprehensive security checks"""
        # Check if account is locked
        if self.is_account_locked(username):
            self.record_login_attempt(username, False, ip_address)
            return False, "Account is temporarily locked due to too many failed attempts"
        
        cursor = self.conn.cursor()
        cursor.execute(
            "SELECT id, password_hash, requires_password_change FROM users WHERE username = ?",
            (username,)
        )
        
        result = cursor.fetchone()
        if not result:
            self.record_login_attempt(username, False, ip_address)
            return False, "Invalid username or password"
        
        user_id, stored_hash, requires_change = result
        
        # Verify password
        if not self.verify_password(password, stored_hash):
            self.record_login_attempt(username, False, ip_address)
            return False, "Invalid username or password"
        
        # Successful login
        self.record_login_attempt(username, True, ip_address)
        
        if requires_change:
            return True, "Login successful - password change required"
        
        self.logger.info(f"User {username} logged in successfully")
        return True, "Login successful"
    
    def change_password(self, username: str, old_password: str, new_password: str) -> Tuple[bool, str]:
        """Change user password with security validation"""
        # Verify current password
        login_success, _ = self.login_user(username, old_password)
        if not login_success:
            return False, "Current password is incorrect"
        
        # Get user ID
        cursor = self.conn.cursor()
        cursor.execute("SELECT id FROM users WHERE username = ?", (username,))
        result = cursor.fetchone()
        if not result:
            return False, "User not found"
        
        user_id = result[0]
        
        # Validate new password strength
        is_valid, message = self.validate_password_strength(new_password, username)
        if not is_valid:
            return False, message
        
        # Check password history
        if not self.check_password_history(user_id, new_password):
            return False, f"Password cannot be one of your last {self.password_history_count} passwords"
        
        # Hash new password
        new_password_hash = self.hash_password(new_password)
        
        # Update password
        cursor.execute(
            """UPDATE users SET 
                 password_hash = ?, 
                 password_created_at = CURRENT_TIMESTAMP,
                 requires_password_change = FALSE,
                 updated_at = CURRENT_TIMESTAMP
               WHERE username = ?""",
            (new_password_hash, username)
        )
        
        # Add to password history
        self.add_to_password_history(user_id, new_password_hash)
        
        self.conn.commit()
        
        self.logger.info(f"Password changed successfully for user {username}")
        return True, "Password changed successfully"
    
    def generate_secure_password(self, length: int = 16) -> str:
        """Generate cryptographically secure password"""
        if length < self.min_password_length:
            length = self.min_password_length
        
        import string
        
        # Character sets
        lowercase = string.ascii_lowercase
        uppercase = string.ascii_uppercase
        digits = string.digits
        special = '!@#$%^&*()_+-=[]{}|;:,.<>?'
        
        # Ensure at least one character from each set
        password = [
            secrets.choice(lowercase),
            secrets.choice(uppercase), 
            secrets.choice(digits),
            secrets.choice(special)
        ]
        
        # Fill the rest with random characters
        all_chars = lowercase + uppercase + digits + special
        for _ in range(length - 4):
            password.append(secrets.choice(all_chars))
        
        # Shuffle the password
        secrets.SystemRandom().shuffle(password)
        
        return ''.join(password)
    
    def get_security_metrics(self) -> dict:
        """Get security metrics for monitoring"""
        cursor = self.conn.cursor()
        
        # Recent failed login attempts
        cursor.execute(
            """SELECT COUNT(*) FROM login_attempts 
               WHERE success = FALSE AND attempted_at > datetime('now', '-1 hour')"""
        )
        recent_failures = cursor.fetchone()[0]
        
        # Locked accounts
        cursor.execute(
            "SELECT COUNT(*) FROM users WHERE locked_until > datetime('now')"
        )
        locked_accounts = cursor.fetchone()[0]
        
        # Users requiring password change
        cursor.execute(
            "SELECT COUNT(*) FROM users WHERE requires_password_change = TRUE"
        )
        require_change = cursor.fetchone()[0]
        
        return {
            'recent_failed_attempts': recent_failures,
            'locked_accounts': locked_accounts,
            'users_requiring_password_change': require_change
        }

# Example usage
if __name__ == "__main__":
    auth_system = SecureAuthSystem()
    
    # Register a user
    success, message = auth_system.register_user("testuser", "MySecureP@ssw0rd123!")
    print(f"Registration: {success} - {message}")
    
    # Login
    success, message = auth_system.login_user("testuser", "MySecureP@ssw0rd123!", "192.168.1.1")
    print(f"Login: {success} - {message}")
    
    # Generate secure password
    secure_pwd = auth_system.generate_secure_password(20)
    print(f"Generated secure password: {secure_pwd}")
    
    # Get security metrics
    metrics = auth_system.get_security_metrics()
    print(f"Security metrics: {metrics}")

💡 Why This Fix Works

The vulnerable implementation uses weak hash functions (MD5, SHA-256 without salt), has no password policy, allows timing attacks, and exposes password hashes. The secure version uses bcrypt with proper work factors, implements comprehensive password policies, includes account lockout mechanisms, maintains password history, and provides security monitoring capabilities.

Why it happens

Developers use fast cryptographic hash functions like MD5, SHA-1, or SHA-256 for password storage. These functions are designed for speed and can be computed millions of times per second, making them vulnerable to brute force and rainbow table attacks.

Root causes

Using Fast Hash Functions for Password Storage

Developers use fast cryptographic hash functions like MD5, SHA-1, or SHA-256 for password storage. These functions are designed for speed and can be computed millions of times per second, making them vulnerable to brute force and rainbow table attacks.

Preview example – JAVA
// Java - VULNERABLE: Using SHA-256 for password hashing
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

public class VulnerablePasswordHashing {
    
    public String hashPassword(String password) {
        try {
            // VULNERABLE: SHA-256 is too fast for password hashing
            MessageDigest digest = MessageDigest.getInstance("SHA-256");
            byte[] hash = digest.digest(password.getBytes());
            
            StringBuilder hexString = new StringBuilder();
            for (byte b : hash) {
                String hex = Integer.toHexString(0xff & b);
                if (hex.length() == 1) {
                    hexString.append('0');
                }
                hexString.append(hex);
            }
            
            return hexString.toString();
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("SHA-256 algorithm not available", e);
        }
    }
    
    public boolean verifyPassword(String password, String storedHash) {
        // VULNERABLE: Direct comparison without timing protection
        return hashPassword(password).equals(storedHash);
    }
    
    // Even worse - MD5 hashing
    public String md5Hash(String password) {
        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            md.update(password.getBytes());
            byte[] digest = md.digest();
            
            StringBuilder sb = new StringBuilder();
            for (byte b : digest) {
                sb.append(String.format("%02x", b & 0xff));
            }
            
            return sb.toString();
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("MD5 algorithm not available", e);
        }
    }
}

Missing or Fixed Salt Values

Password hashing without salts or using fixed/predictable salts. This allows attackers to use precomputed rainbow tables and makes identical passwords produce identical hashes, revealing password patterns across users.

Preview example – PYTHON
# Python - VULNERABLE: Password hashing without proper salting
import hashlib
import hmac

class WeakPasswordSecurity:
    # DANGEROUS: Fixed salt used for all passwords
    FIXED_SALT = "app_salt_123456789"
    
    def hash_password_no_salt(self, password):
        """VULNERABLE: No salt at all"""
        return hashlib.sha256(password.encode()).hexdigest()
    
    def hash_password_fixed_salt(self, password):
        """VULNERABLE: Fixed salt for all users"""
        salted_password = password + self.FIXED_SALT
        return hashlib.sha256(salted_password.encode()).hexdigest()
    
    def hash_password_predictable_salt(self, password, username):
        """VULNERABLE: Predictable salt based on username"""
        salt = hashlib.md5(username.encode()).hexdigest()[:8]
        salted_password = password + salt
        return hashlib.sha256(salted_password.encode()).hexdigest(), salt
    
    def verify_password_weak(self, password, stored_hash, username=None):
        """VULNERABLE: Weak verification methods"""
        if username:
            computed_hash, _ = self.hash_password_predictable_salt(password, username)
        else:
            computed_hash = self.hash_password_fixed_salt(password)
        
        # Timing attack vulnerable comparison
        return computed_hash == stored_hash
    
    def hash_with_rounds_but_weak(self, password, rounds=1000):
        """VULNERABLE: Multiple rounds of weak hashing"""
        hash_value = password.encode()
        
        # Multiple rounds of SHA-256 - still vulnerable
        for i in range(rounds):
            hash_value = hashlib.sha256(hash_value).digest()
        
        return hash_value.hex()
    
    def pbkdf2_with_weak_params(self, password, salt):
        """VULNERABLE: PBKDF2 with insufficient iterations"""
        # Only 1000 iterations - too low for modern security
        return hashlib.pbkdf2_hmac('sha256', password.encode(), salt, 1000)

Custom Password Hashing Implementations

Developers implementing their own password hashing schemes or incorrectly implementing known algorithms. Custom crypto is almost always flawed and should be avoided in favor of well-tested, standard implementations.

Preview example – CSHARP
// C# - VULNERABLE: Custom password hashing implementation
using System;
using System.Security.Cryptography;
using System.Text;

public class CustomPasswordHashing
{
    // VULNERABLE: Custom hashing scheme
    public string CustomHashPassword(string password, string username)
    {
        // DANGEROUS: Custom algorithm that's easily broken
        var combined = password + username.Reverse() + DateTime.Now.Year.ToString();
        
        using (var sha1 = SHA1.Create())
        {
            var hash1 = sha1.ComputeHash(Encoding.UTF8.GetBytes(combined));
            
            // XOR with password bytes (weak obscurity)
            var passwordBytes = Encoding.UTF8.GetBytes(password);
            for (int i = 0; i < Math.Min(hash1.Length, passwordBytes.Length); i++)
            {
                hash1[i] ^= passwordBytes[i % passwordBytes.Length];
            }
            
            return Convert.ToBase64String(hash1);
        }
    }
    
    // VULNERABLE: Weak key stretching
    public string StretchPassword(string password, int iterations = 100)
    {
        var result = password;
        
        // DANGEROUS: Too few iterations and weak method
        for (int i = 0; i < iterations; i++)
        {
            using (var md5 = MD5.Create())
            {
                var hash = md5.ComputeHash(Encoding.UTF8.GetBytes(result + i.ToString()));
                result = Convert.ToBase64String(hash);
            }
        }
        
        return result;
    }
    
    // VULNERABLE: Encryption instead of hashing
    public string EncryptPassword(string password)
    {
        // NEVER encrypt passwords - always hash them!
        using (var aes = Aes.Create())
        {
            // Hardcoded key - multiple vulnerabilities
            aes.Key = Encoding.UTF8.GetBytes("MySecretKey12345").Take(32).ToArray();
            aes.IV = new byte[16]; // Zero IV - deterministic encryption
            
            using (var encryptor = aes.CreateEncryptor())
            {
                var passwordBytes = Encoding.UTF8.GetBytes(password);
                var encrypted = encryptor.TransformFinalBlock(passwordBytes, 0, passwordBytes.Length);
                return Convert.ToBase64String(encrypted);
            }
        }
    }
    
    // VULNERABLE: Reversible "hashing"
    public string ReversibleHash(string password)
    {
        // DANGEROUS: This is encoding, not hashing!
        var bytes = Encoding.UTF8.GetBytes(password);
        
        // Simple XOR with fixed key
        byte key = 0x42;
        for (int i = 0; i < bytes.Length; i++)
        {
            bytes[i] ^= key;
        }
        
        return Convert.ToBase64String(bytes);
    }
}

Insufficient Computational Cost

Using password hashing algorithms with insufficient computational cost or work factors. Even when using proper algorithms like bcrypt or PBKDF2, setting the work factor too low makes passwords vulnerable to brute force attacks with modern hardware.

Preview example – JAVASCRIPT
// Node.js - VULNERABLE: Weak parameters for password hashing
const bcrypt = require('bcrypt');
const crypto = require('crypto');

class WeakPasswordHashing {
    
    async hashPasswordWeakBcrypt(password) {
        // VULNERABLE: bcrypt with too low salt rounds
        const saltRounds = 4; // WAY too low - should be 12+ in 2023
        return await bcrypt.hash(password, saltRounds);
    }
    
    hashPasswordWeakPBKDF2(password, salt) {
        // VULNERABLE: PBKDF2 with insufficient iterations
        const iterations = 1000; // Too low - should be 100,000+
        const keyLength = 32;
        
        return crypto.pbkdf2Sync(password, salt, iterations, keyLength, 'sha256');
    }
    
    hashPasswordSimpleHash(password) {
        // VULNERABLE: Single round of hashing
        return crypto.createHash('sha256').update(password).digest('hex');
    }
    
    // VULNERABLE: Custom implementation with weak work factor
    customSlowHash(password, salt, iterations = 500) {
        let result = password + salt;
        
        // Too few iterations for meaningful protection
        for (let i = 0; i < iterations; i++) {
            result = crypto.createHash('sha256').update(result).digest('hex');
        }
        
        return result;
    }
    
    // VULNERABLE: Using scrypt with weak parameters
    hashPasswordWeakScrypt(password, salt) {
        // VULNERABLE: Scrypt with insufficient parameters
        const N = 1024;     // Too low - should be 32768+
        const r = 1;        // Too low - should be 8+
        const p = 1;        // Acceptable for p
        const keyLen = 32;
        
        return crypto.scryptSync(password, salt, keyLen, { N, r, p });
    }
    
    // VULNERABLE: Time-based verification
    async verifyPasswordInsecure(password, hashedPassword) {
        // VULNERABLE: Timing attack possible
        const computedHash = await this.hashPasswordWeakBcrypt(password);
        
        // String comparison vulnerable to timing attacks
        return computedHash === hashedPassword;
    }
}

Fixes

1

Use bcrypt with Appropriate Work Factor

Implement bcrypt password hashing with a work factor of at least 12 (as of 2023). bcrypt automatically handles salt generation and is designed to be slow and memory-hard, making it resistant to brute force attacks even with specialized hardware.

View implementation – PYTHON
# Python - SECURE: bcrypt implementation with proper work factor
import bcrypt
import secrets
import time
from typing import Tuple, Optional

class SecurePasswordHashing:
    # Minimum work factor - adjust based on current year and hardware
    MIN_ROUNDS = 12  # As of 2023, 12 is minimum, 14+ is better
    
    def __init__(self, rounds: int = 14):
        """Initialize with secure default rounds"""
        if rounds < self.MIN_ROUNDS:
            raise ValueError(f"Work factor must be at least {self.MIN_ROUNDS}")
        
        self.rounds = rounds
        
        # Test performance to ensure reasonable response time
        self._validate_performance()
    
    def _validate_performance(self):
        """Test hashing performance to ensure it's reasonable"""
        test_password = "test_password_for_performance"
        
        start_time = time.time()
        bcrypt.hashpw(test_password.encode('utf-8'), bcrypt.gensalt(rounds=self.rounds))
        end_time = time.time()
        
        duration = end_time - start_time
        
        if duration > 5.0:  # More than 5 seconds might be too slow
            print(f"Warning: Password hashing takes {duration:.2f} seconds")
        elif duration < 0.1:  # Less than 100ms might be too fast
            print(f"Warning: Password hashing only takes {duration:.3f} seconds - consider higher rounds")
    
    def hash_password(self, password: str) -> str:
        """Hash password using bcrypt with secure parameters"""
        if not password:
            raise ValueError("Password cannot be empty")
        
        # Encode password to bytes
        password_bytes = password.encode('utf-8')
        
        # Generate salt and hash
        salt = bcrypt.gensalt(rounds=self.rounds)
        hashed = bcrypt.hashpw(password_bytes, salt)
        
        # Return as string for storage
        return hashed.decode('utf-8')
    
    def verify_password(self, password: str, hashed_password: str) -> bool:
        """Verify password against hash using timing-safe comparison"""
        if not password or not hashed_password:
            return False
        
        try:
            password_bytes = password.encode('utf-8')
            hashed_bytes = hashed_password.encode('utf-8')
            
            # bcrypt.checkpw includes timing-safe comparison
            return bcrypt.checkpw(password_bytes, hashed_bytes)
        
        except (ValueError, TypeError) as e:
            # Log the error but don't reveal details
            print(f"Password verification error: {type(e).__name__}")
            return False
    
    def needs_rehash(self, hashed_password: str) -> bool:
        """Check if password hash needs to be updated"""
        try:
            # Extract current rounds from hash
            if hashed_password.startswith('$2b$'):
                parts = hashed_password.split('$')
                if len(parts) >= 3:
                    current_rounds = int(parts[2])
                    return current_rounds < self.rounds
            
            return True  # Unknown format, needs rehash
        
        except (ValueError, IndexError):
            return True
    
    def update_password_hash(self, password: str, old_hash: str) -> Optional[str]:
        """Update password hash if verification succeeds and rehash is needed"""
        if not self.verify_password(password, old_hash):
            return None
        
        if self.needs_rehash(old_hash):
            return self.hash_password(password)
        
        return old_hash  # No update needed
    
    @staticmethod
    def generate_secure_temporary_password(length: int = 16) -> str:
        """Generate secure temporary password"""
        import string
        
        if length < 12:
            raise ValueError("Temporary password must be at least 12 characters")
        
        # Use cryptographically secure random
        alphabet = string.ascii_letters + string.digits + "!@#$%^&*"
        return ''.join(secrets.choice(alphabet) for _ in range(length))
2

Implement Argon2 for Maximum Security

Use Argon2id, the winner of the Password Hashing Competition, for new applications. Argon2id provides the best resistance against both side-channel and time-memory trade-off attacks. Configure with appropriate memory, time, and parallelism parameters.

View implementation – JAVA
// Java - SECURE: Argon2id implementation with proper parameters
import de.mkammerer.argon2.Argon2Factory;
import de.mkammerer.argon2.Argon2;
import java.security.SecureRandom;
import java.util.Base64;

public class SecureArgon2PasswordHashing {
    private final Argon2 argon2;
    private final int iterations;
    private final int memory;
    private final int parallelism;
    private final SecureRandom secureRandom;
    
    public SecureArgon2PasswordHashing() {
        // Use Argon2id (hybrid version) for best security
        this.argon2 = Argon2Factory.create(Argon2Factory.Argon2Types.ARGON2id);
        
        // Configure secure parameters (adjust based on your hardware)
        this.iterations = 3;      // Number of iterations
        this.memory = 65536;      // Memory usage in KB (64 MB)
        this.parallelism = 4;     // Number of parallel threads
        
        this.secureRandom = new SecureRandom();
        
        // Validate performance
        validatePerformance();
    }
    
    public SecureArgon2PasswordHashing(int iterations, int memory, int parallelism) {
        this.argon2 = Argon2Factory.create(Argon2Factory.Argon2Types.ARGON2id);
        this.iterations = iterations;
        this.memory = memory;
        this.parallelism = parallelism;
        this.secureRandom = new SecureRandom();
        
        validateParameters();
        validatePerformance();
    }
    
    private void validateParameters() {
        if (iterations < 2) {
            throw new IllegalArgumentException("Iterations must be at least 2");
        }
        if (memory < 8192) { // Minimum 8 MB
            throw new IllegalArgumentException("Memory must be at least 8192 KB");
        }
        if (parallelism < 1 || parallelism > 16) {
            throw new IllegalArgumentException("Parallelism must be between 1 and 16");
        }
    }
    
    private void validatePerformance() {
        String testPassword = "test_password_for_performance_validation";
        
        long startTime = System.currentTimeMillis();
        String hash = hashPassword(testPassword);
        long hashTime = System.currentTimeMillis() - startTime;
        
        startTime = System.currentTimeMillis();
        verifyPassword(testPassword, hash);
        long verifyTime = System.currentTimeMillis() - startTime;
        
        System.out.printf("Argon2 performance: Hash=%dms, Verify=%dms%n", hashTime, verifyTime);
        
        if (hashTime > 5000) { // More than 5 seconds
            System.out.println("Warning: Password hashing is very slow. Consider reducing parameters.");
        } else if (hashTime < 100) { // Less than 100ms
            System.out.println("Warning: Password hashing is fast. Consider increasing parameters.");
        }
    }
    
    public String hashPassword(String password) {
        if (password == null || password.isEmpty()) {
            throw new IllegalArgumentException("Password cannot be null or empty");
        }
        
        try {
            // Generate salt
            byte[] salt = new byte[32]; // 256-bit salt
            secureRandom.nextBytes(salt);
            
            // Hash password with Argon2id
            String hash = argon2.hash(iterations, memory, parallelism, password.toCharArray(), salt);
            
            return hash;
        } catch (Exception e) {
            throw new RuntimeException("Password hashing failed", e);
        }
    }
    
    public boolean verifyPassword(String password, String hash) {
        if (password == null || hash == null) {
            return false;
        }
        
        try {
            // Argon2 verify includes timing-safe comparison
            return argon2.verify(hash, password.toCharArray());
        } catch (Exception e) {
            // Log error but don't reveal details
            System.err.println("Password verification error: " + e.getClass().getSimpleName());
            return false;
        }
    }
    
    public boolean needsRehash(String hash) {
        if (hash == null || !hash.startsWith("$argon2id$")) {
            return true;
        }
        
        try {
            // Parse hash parameters
            String[] parts = hash.split("\\$");
            if (parts.length < 4) {
                return true;
            }
            
            String paramString = parts[3];
            
            // Extract parameters (simplified parsing)
            int hashIterations = extractParameter(paramString, "t=");
            int hashMemory = extractParameter(paramString, "m=");
            int hashParallelism = extractParameter(paramString, "p=");
            
            // Check if current parameters are stronger
            return hashIterations < iterations || 
                   hashMemory < memory || 
                   hashParallelism < parallelism;
        } catch (Exception e) {
            return true; // If we can't parse, assume it needs rehash
        }
    }
    
    private int extractParameter(String paramString, String prefix) {
        int start = paramString.indexOf(prefix);
        if (start == -1) {
            return 0;
        }
        
        start += prefix.length();
        int end = paramString.indexOf(',', start);
        if (end == -1) {
            end = paramString.length();
        }
        
        return Integer.parseInt(paramString.substring(start, end));
    }
    
    public String updatePasswordHash(String password, String oldHash) {
        if (!verifyPassword(password, oldHash)) {
            return null; // Verification failed
        }
        
        if (needsRehash(oldHash)) {
            return hashPassword(password);
        }
        
        return oldHash; // No update needed
    }
    
    public void cleanup() {
        // Clear sensitive data
        if (argon2 != null) {
            // Argon2 library handles cleanup automatically
        }
    }
    
    // Factory method for different security levels
    public static SecureArgon2PasswordHashing createForSecurityLevel(SecurityLevel level) {
        switch (level) {
            case INTERACTIVE:
                return new SecureArgon2PasswordHashing(2, 19456, 1);   // ~20ms
            case STANDARD:
                return new SecureArgon2PasswordHashing(3, 65536, 4);   // ~100ms
            case HIGH:
                return new SecureArgon2PasswordHashing(4, 262144, 8);  // ~500ms
            case MAXIMUM:
                return new SecureArgon2PasswordHashing(5, 1048576, 16); // ~2s
            default:
                return new SecureArgon2PasswordHashing(); // Standard
        }
    }
    
    public enum SecurityLevel {
        INTERACTIVE, STANDARD, HIGH, MAXIMUM
    }
}
3

Implement PBKDF2 with High Iteration Count

When bcrypt or Argon2 are not available, use PBKDF2 with a high iteration count (100,000+ iterations as of 2023) and cryptographically secure salt generation. Always use HMAC-SHA256 or stronger as the pseudorandom function.

View implementation – CSHARP
// C# - SECURE: PBKDF2 implementation with high iteration count
using System;
using System.Security.Cryptography;
using System.Text;
using System.Linq;

public class SecurePBKDF2PasswordHashing
{
    private const int MinIterations = 100000; // Minimum for 2023
    private const int SaltSize = 32; // 256-bit salt
    private const int HashSize = 32; // 256-bit hash
    
    private readonly int iterations;
    private readonly RNGCryptoServiceProvider rng;
    
    public SecurePBKDF2PasswordHashing(int iterations = 320000)
    {
        if (iterations < MinIterations)
        {
            throw new ArgumentException($"Iterations must be at least {MinIterations}");
        }
        
        this.iterations = iterations;
        this.rng = new RNGCryptoServiceProvider();
        
        // Validate performance
        ValidatePerformance();
    }
    
    private void ValidatePerformance()
    {
        var testPassword = "test_password_for_performance";
        var testSalt = GenerateSalt();
        
        var stopwatch = System.Diagnostics.Stopwatch.StartNew();
        DeriveKey(testPassword, testSalt);
        stopwatch.Stop();
        
        Console.WriteLine($"PBKDF2 performance: {stopwatch.ElapsedMilliseconds}ms for {iterations} iterations");
        
        if (stopwatch.ElapsedMilliseconds > 5000)
        {
            Console.WriteLine("Warning: Password hashing is very slow. Consider reducing iterations.");
        }
        else if (stopwatch.ElapsedMilliseconds < 100)
        {
            Console.WriteLine("Warning: Password hashing is fast. Consider increasing iterations.");
        }
    }
    
    public byte[] GenerateSalt()
    {
        var salt = new byte[SaltSize];
        rng.GetBytes(salt);
        return salt;
    }
    
    private byte[] DeriveKey(string password, byte[] salt)
    {
        using (var pbkdf2 = new Rfc2898DeriveBytes(password, salt, iterations, HashAlgorithmName.SHA256))
        {
            return pbkdf2.GetBytes(HashSize);
        }
    }
    
    public string HashPassword(string password)
    {
        if (string.IsNullOrEmpty(password))
        {
            throw new ArgumentException("Password cannot be null or empty");
        }
        
        try
        {
            // Generate random salt
            var salt = GenerateSalt();
            
            // Derive key using PBKDF2
            var hash = DeriveKey(password, salt);
            
            // Combine salt, iterations, and hash for storage
            var result = new PasswordHash
            {
                Salt = salt,
                Hash = hash,
                Iterations = iterations,
                Algorithm = "PBKDF2-SHA256"
            };
            
            return SerializePasswordHash(result);
        }
        catch (Exception ex)
        {
            throw new InvalidOperationException("Password hashing failed", ex);
        }
    }
    
    public bool VerifyPassword(string password, string storedHash)
    {
        if (string.IsNullOrEmpty(password) || string.IsNullOrEmpty(storedHash))
        {
            return false;
        }
        
        try
        {
            // Deserialize stored hash
            var passwordHash = DeserializePasswordHash(storedHash);
            
            // Derive key with same parameters
            var computedHash = DeriveKey(password, passwordHash.Salt, passwordHash.Iterations);
            
            // Timing-safe comparison
            return TimingSafeEquals(computedHash, passwordHash.Hash);
        }
        catch (Exception ex)
        {
            // Log error but don't reveal details
            Console.WriteLine($"Password verification error: {ex.GetType().Name}");
            return false;
        }
    }
    
    private byte[] DeriveKey(string password, byte[] salt, int iterationCount)
    {
        using (var pbkdf2 = new Rfc2898DeriveBytes(password, salt, iterationCount, HashAlgorithmName.SHA256))
        {
            return pbkdf2.GetBytes(HashSize);
        }
    }
    
    private bool TimingSafeEquals(byte[] a, byte[] b)
    {
        if (a.Length != b.Length)
        {
            return false;
        }
        
        var result = 0;
        for (int i = 0; i < a.Length; i++)
        {
            result |= a[i] ^ b[i];
        }
        
        return result == 0;
    }
    
    public bool NeedsRehash(string storedHash)
    {
        try
        {
            var passwordHash = DeserializePasswordHash(storedHash);
            
            // Check if iterations are below current standard
            return passwordHash.Iterations < iterations || 
                   passwordHash.Algorithm != "PBKDF2-SHA256";
        }
        catch
        {
            return true; // If we can't parse, assume it needs rehash
        }
    }
    
    public string UpdatePasswordHash(string password, string oldHash)
    {
        if (!VerifyPassword(password, oldHash))
        {
            return null; // Verification failed
        }
        
        if (NeedsRehash(oldHash))
        {
            return HashPassword(password);
        }
        
        return oldHash; // No update needed
    }
    
    private string SerializePasswordHash(PasswordHash passwordHash)
    {
        // Format: algorithm$iterations$salt$hash (all base64 encoded)
        var saltBase64 = Convert.ToBase64String(passwordHash.Salt);
        var hashBase64 = Convert.ToBase64String(passwordHash.Hash);
        
        return $"{passwordHash.Algorithm}${passwordHash.Iterations}${saltBase64}${hashBase64}";
    }
    
    private PasswordHash DeserializePasswordHash(string storedHash)
    {
        var parts = storedHash.Split('$');
        
        if (parts.Length != 4)
        {
            throw new ArgumentException("Invalid hash format");
        }
        
        return new PasswordHash
        {
            Algorithm = parts[0],
            Iterations = int.Parse(parts[1]),
            Salt = Convert.FromBase64String(parts[2]),
            Hash = Convert.FromBase64String(parts[3])
        };
    }
    
    protected virtual void Dispose(bool disposing)
    {
        if (disposing)
        {
            rng?.Dispose();
        }
    }
    
    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }
    
    private class PasswordHash
    {
        public string Algorithm { get; set; }
        public int Iterations { get; set; }
        public byte[] Salt { get; set; }
        public byte[] Hash { get; set; }
    }
}
4

Implement Secure Password Policy and Monitoring

Combine strong password hashing with comprehensive password policies, breach monitoring, and secure password reset mechanisms. Implement rate limiting, account lockouts, and monitoring for password spraying attacks.

View implementation – GO
// Go - SECURE: Comprehensive password security with policy enforcement
package main

import (
	"crypto/rand"
	"crypto/subtle"
	"encoding/base64"
	"errors"
	"fmt"
	"log"
	"regexp"
	"strings"
	"time"
	"unicode"

	"golang.org/x/crypto/argon2"
)

type PasswordHasher struct {
	time    uint32
	memory  uint32
	threads uint8
	keyLen  uint32
	saltLen uint32
}

type HashedPassword struct {
	Hash      []byte
	Salt      []byte
	Time      uint32
	Memory    uint32
	Threads   uint8
	Algorithm string
	CreatedAt time.Time
}

type PasswordPolicy struct {
	MinLength        int
	MaxLength        int
	RequireUpper     bool
	RequireLower     bool
	RequireDigits    bool
	RequireSpecial   bool
	BlockCommon      bool
	BlockRepeating   bool
	MaxAge           time.Duration
	PreventReuse     int // Number of previous passwords to check
}

type PasswordSecurityService struct {
	hasher           *PasswordHasher
	policy           *PasswordPolicy
	commonPasswords  map[string]bool
	breachedHashes   map[string]bool // In production, use external service
	loginAttempts    map[string][]time.Time
	maxAttempts      int
	lockoutDuration  time.Duration
}

func NewPasswordSecurityService() *PasswordSecurityService {
	return &PasswordSecurityService{
		hasher: &PasswordHasher{
			time:    3,      // 3 iterations
			memory:  64 * 1024, // 64 MB
			threads: 4,      // 4 threads
			keyLen:  32,     // 32 bytes
			saltLen: 32,     // 32 bytes
		},
		policy: &PasswordPolicy{
			MinLength:      12,
			MaxLength:      128,
			RequireUpper:   true,
			RequireLower:   true,
			RequireDigits:  true,
			RequireSpecial: true,
			BlockCommon:    true,
			BlockRepeating: true,
			MaxAge:         90 * 24 * time.Hour, // 90 days
			PreventReuse:   5, // Last 5 passwords
		},
		commonPasswords: loadCommonPasswords(),
		breachedHashes:  make(map[string]bool),
		loginAttempts:   make(map[string][]time.Time),
		maxAttempts:     5,
		lockoutDuration: 15 * time.Minute,
	}
}

func (p *PasswordHasher) generateSalt() ([]byte, error) {
	salt := make([]byte, p.saltLen)
	_, err := rand.Read(salt)
	if err != nil {
		return nil, fmt.Errorf("failed to generate salt: %w", err)
	}
	return salt, nil
}

func (p *PasswordHasher) HashPassword(password string) (*HashedPassword, error) {
	if password == "" {
		return nil, errors.New("password cannot be empty")
	}

	salt, err := p.generateSalt()
	if err != nil {
		return nil, err
	}

	// Use Argon2id for hashing
	hash := argon2.IDKey([]byte(password), salt, p.time, p.memory, p.threads, p.keyLen)

	return &HashedPassword{
		Hash:      hash,
		Salt:      salt,
		Time:      p.time,
		Memory:    p.memory,
		Threads:   p.threads,
		Algorithm: "argon2id",
		CreatedAt: time.Now(),
	}, nil
}

func (p *PasswordHasher) VerifyPassword(password string, stored *HashedPassword) bool {
	if password == "" || stored == nil {
		return false
	}

	// Recompute hash with stored parameters
	computedHash := argon2.IDKey(
		[]byte(password),
		stored.Salt,
		stored.Time,
		stored.Memory,
		stored.Threads,
		uint32(len(stored.Hash)),
	)

	// Timing-safe comparison
	return subtle.ConstantTimeCompare(stored.Hash, computedHash) == 1
}

func (s *PasswordSecurityService) ValidatePasswordPolicy(password string) error {
	policy := s.policy

	// Check length
	if len(password) < policy.MinLength {
		return fmt.Errorf("password must be at least %d characters", policy.MinLength)
	}
	if len(password) > policy.MaxLength {
		return fmt.Errorf("password must be no more than %d characters", policy.MaxLength)
	}

	// Check character requirements
	var hasUpper, hasLower, hasDigit, hasSpecial bool
	for _, char := range password {
		switch {
		case unicode.IsUpper(char):
			hasUpper = true
		case unicode.IsLower(char):
			hasLower = true
		case unicode.IsDigit(char):
			hasDigit = true
		case unicode.IsPunct(char) || unicode.IsSymbol(char):
			hasSpecial = true
		}
	}

	if policy.RequireUpper && !hasUpper {
		return errors.New("password must contain at least one uppercase letter")
	}
	if policy.RequireLower && !hasLower {
		return errors.New("password must contain at least one lowercase letter")
	}
	if policy.RequireDigits && !hasDigit {
		return errors.New("password must contain at least one digit")
	}
	if policy.RequireSpecial && !hasSpecial {
		return errors.New("password must contain at least one special character")
	}

	// Check for common passwords
	if policy.BlockCommon && s.isCommonPassword(password) {
		return errors.New("password is too common")
	}

	// Check for repeating patterns
	if policy.BlockRepeating && hasRepeatingPattern(password) {
		return errors.New("password contains repeating patterns")
	}

	// Check against known breaches
	if s.isPasswordBreached(password) {
		return errors.New("password has been found in data breaches")
	}

	return nil
}

func (s *PasswordSecurityService) isCommonPassword(password string) bool {
	// Check against common passwords list
	lowerPassword := strings.ToLower(password)
	return s.commonPasswords[lowerPassword]
}

func (s *PasswordSecurityService) isPasswordBreached(password string) bool {
	// In production, this would query HaveIBeenPwned API or similar
	// For demo, we'll use a simple hash check
	hash := fmt.Sprintf("%x", password) // Simplified
	return s.breachedHashes[hash]
}

func hasRepeatingPattern(password string) bool {
	// Check for simple repeating patterns
	for i := 1; i <= len(password)/2; i++ {
		pattern := password[:i]
		repeated := strings.Repeat(pattern, len(password)/i)
		if strings.HasPrefix(password, repeated) {
			return true
		}
	}

	// Check for consecutive characters
	consecutiveCount := 1
	for i := 1; i < len(password); i++ {
		if password[i] == password[i-1] {
			consecutiveCount++
			if consecutiveCount >= 3 {
				return true
			}
		} else {
			consecutiveCount = 1
		}
	}

	return false
}

func (s *PasswordSecurityService) CreatePassword(password string) (*HashedPassword, error) {
	// Validate against policy
	if err := s.ValidatePasswordPolicy(password); err != nil {
		return nil, err
	}

	// Hash the password
	return s.hasher.HashPassword(password)
}

func (s *PasswordSecurityService) AuthenticateUser(userID, password string, storedHash *HashedPassword) (bool, error) {
	// Check for account lockout
	if s.isAccountLocked(userID) {
		return false, errors.New("account is locked due to too many failed attempts")
	}

	// Verify password
	isValid := s.hasher.VerifyPassword(password, storedHash)

	if !isValid {
		// Record failed attempt
		s.recordFailedAttempt(userID)
		return false, nil
	}

	// Clear failed attempts on successful login
	s.clearFailedAttempts(userID)

	// Check if password needs rehashing
	if s.needsRehash(storedHash) {
		log.Printf("User %s password needs rehashing", userID)
	}

	return true, nil
}

func (s *PasswordSecurityService) isAccountLocked(userID string) bool {
	attempts := s.loginAttempts[userID]
	if len(attempts) < s.maxAttempts {
		return false
	}

	// Check if recent attempts exceed threshold
	cutoff := time.Now().Add(-s.lockoutDuration)
	recentAttempts := 0
	for _, attempt := range attempts {
		if attempt.After(cutoff) {
			recentAttempts++
		}
	}

	return recentAttempts >= s.maxAttempts
}

func (s *PasswordSecurityService) recordFailedAttempt(userID string) {
	now := time.Now()
	s.loginAttempts[userID] = append(s.loginAttempts[userID], now)

	// Keep only recent attempts
	cutoff := now.Add(-24 * time.Hour)
	filtered := make([]time.Time, 0)
	for _, attempt := range s.loginAttempts[userID] {
		if attempt.After(cutoff) {
			filtered = append(filtered, attempt)
		}
	}
	s.loginAttempts[userID] = filtered
}

func (s *PasswordSecurityService) clearFailedAttempts(userID string) {
	delete(s.loginAttempts, userID)
}

func (s *PasswordSecurityService) needsRehash(stored *HashedPassword) bool {
	// Check if parameters are below current standards
	return stored.Time < s.hasher.time ||
		stored.Memory < s.hasher.memory ||
		stored.Threads < s.hasher.threads ||
		stored.Algorithm != "argon2id"
}

func (s *PasswordSecurityService) GenerateSecurePassword(length int) (string, error) {
	if length < s.policy.MinLength {
		length = s.policy.MinLength
	}

	const (
		uppercase = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
		lowercase = "abcdefghijklmnopqrstuvwxyz"
		digits    = "0123456789"
		special   = "!@#$%^&*()_+-=[]{}|;:,.<>?"
	)

	allChars := uppercase + lowercase + digits + special
	password := make([]byte, length)

	// Ensure at least one of each required type
	password[0] = uppercase[secureRandomInt(len(uppercase))]
	password[1] = lowercase[secureRandomInt(len(lowercase))]
	password[2] = digits[secureRandomInt(len(digits))]
	password[3] = special[secureRandomInt(len(special))]

	// Fill the rest randomly
	for i := 4; i < length; i++ {
		password[i] = allChars[secureRandomInt(len(allChars))]
	}

	// Shuffle the password
	for i := len(password) - 1; i > 0; i-- {
		j := secureRandomInt(i + 1)
		password[i], password[j] = password[j], password[i]
	}

	return string(password), nil
}

func secureRandomInt(max int) int {
	b := make([]byte, 4)
	_, err := rand.Read(b)
	if err != nil {
		panic("failed to generate random number")
	}

	return int(uint32(b[0])<<24|uint32(b[1])<<16|uint32(b[2])<<8|uint32(b[3])) % max
}

func loadCommonPasswords() map[string]bool {
	// In production, load from a file or database
	common := []string{
		"password", "123456", "password123", "admin", "qwerty",
		"letmein", "welcome", "monkey", "1234567890", "abc123",
	}

	commonMap := make(map[string]bool)
	for _, pwd := range common {
		commonMap[strings.ToLower(pwd)] = true
	}

	return commonMap
}

func main() {
	service := NewPasswordSecurityService()

	// Example usage
	password := "MySecureP@ssw0rd123"

	// Create password hash
	hashed, err := service.CreatePassword(password)
	if err != nil {
		log.Fatalf("Failed to create password: %v", err)
	}

	fmt.Printf("Password hashed successfully\n")
	fmt.Printf("Algorithm: %s\n", hashed.Algorithm)
	fmt.Printf("Parameters: time=%d, memory=%d, threads=%d\n", 
		hashed.Time, hashed.Memory, hashed.Threads)

	// Verify password
	isValid, err := service.AuthenticateUser("user123", password, hashed)
	if err != nil {
		log.Printf("Authentication error: %v", err)
	}

	fmt.Printf("Password verification: %t\n", isValid)

	// Generate secure password
	generated, err := service.GenerateSecurePassword(16)
	if err != nil {
		log.Printf("Failed to generate password: %v", err)
	} else {
		fmt.Printf("Generated secure password: %s\n", generated)
	}
}

Detect This Vulnerability in Your Code

Sourcery automatically identifies weak password hashing algorithms and practices and many other security issues in your codebase.