> ## Documentation Index
> Fetch the complete documentation index at: https://guide.codepure.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Insufficient Password Hash Effort

> Mitigation for using fast password hashes (SHA256/512) instead of slow ones (bcrypt/Argon2/PBKDF2) in various frameworks.

## Overview

This vulnerability occurs when an application uses a password hashing algorithm that is too fast, even if it's cryptographically strong for other purposes (like SHA-256 or SHA-512). Fast hashes allow attackers to perform rapid offline brute-force or dictionary attacks if they obtain a database of password hashes. Modern password hashing requires algorithms designed to be **computationally expensive** (slow) and **memory-hard** to significantly hinder attackers.

## Business Impact

If an attacker steals password hashes stored using fast algorithms, they can quickly crack many of them, especially common or weak passwords. This leads to widespread account compromise, allowing attackers to impersonate users, steal sensitive data linked to those accounts, and potentially pivot to other systems. 🔑💥

<Card title="Reference Details" icon="book-open" iconType="solid">
  **CWE ID:** [CWE-916](https://cwe.mitre.org/data/definitions/916.html)
  **OWASP Top 10 (2021):** A02:2021 - Cryptographic Failures
  **Severity:** High
</Card>

## Framework-Specific Analysis and Remediation

Secure password hashing relies on algorithms like **bcrypt**, **scrypt**, **Argon2**, or **PBKDF2**. The vulnerability arises when developers manually implement hashing using fast algorithms like SHA-256 directly, often combined with a salt but without sufficient iterations or work factor. Framework defaults are usually secure, but custom implementations or legacy code are common sources of this weakness.

<Tabs>
  <Tab title="Python">
    #### Framework Context

    Django defaults to PBKDF2\_SHA256, which is acceptable but slower is better. Using `hashlib.sha256` directly is the vulnerability.

    #### Vulnerable Scenario 1: Direct SHA-256 Hashing

    A custom user model uses `hashlib.sha256` directly with just a salt.

    ```python theme={null}
    # models.py (Custom User Model)
    import hashlib
    import os

    def set_password(self, raw_password):
        self.salt = os.urandom(16)
        # DANGEROUS: SHA-256 is too fast for passwords, even with a salt.
        # Attackers can compute billions of hashes per second.
        self.password_hash = hashlib.sha256(self.salt + raw_password.encode()).hexdigest()

    def check_password(self, raw_password):
        # Assuming salt is stored separately on the self object
        check_hash = hashlib.sha256(self.salt + raw_password.encode()).hexdigest()
        return check_hash == self.password_hash
    ```

    #### Vulnerable Scenario 2: Using PBKDF2 with Low Iterations

    Using `hashlib.pbkdf2_hmac` but setting the iteration count too low.

    ```python theme={null}
    # utils/auth.py
    import hashlib
    import os

    def hash_password_weak_pbkdf2(password):
        salt = os.urandom(16)
        # DANGEROUS: iterations=1000 is far too low for modern hardware.
        # OWASP recommends at least 600,000 for PBKDF2-SHA256 as of late 2023.
        # Check current OWASP recommendations.
        pw_hash = hashlib.pbkdf2_hmac('sha256', password.encode(), salt, 1000)
        # Store salt with hash, format: salt_hex$hash_hex
        return salt.hex() + '$' + pw_hash.hex()
    ```

    #### Mitigation and Best Practices

    Use Django's built-in password management (`user.set_password(raw_password)`, `user.check_password(raw_password)`). Configure `PASSWORD_HASHERS` in `settings.py` to prioritize `Argon2PasswordHasher` or `BCryptSHA256PasswordHasher`. Ensure PBKDF2 iterations are high if used.

    #### Secure Code Example

    ```python theme={null}
    # settings.py (Secure Password Hashers)
    PASSWORD_HASHERS = [
        'django.contrib.auth.hashers.Argon2PasswordHasher', # Preferred
        'django.contrib.auth.hashers.BCryptSHA256PasswordHasher',
        'django.contrib.auth.hashers.PBKDF2PasswordHasher', # Default, ensure high iterations via Django version
    ]

    # Usage with Django User model (Secure)
    from django.contrib.auth.models import User # Assuming standard User model

    # Assuming 'user' is a retrieved User instance
    # user = User.objects.get(...)
    user.set_password('newS3cureP@ssw0rd') # Uses hasher from settings
    user.save()

    is_correct = user.check_password('attempted_password')
    ```

    #### Testing Strategy

    Inspect password hashes in the database. They should start with `argon2$`, `bcrypt$`, or `pbkdf2_sha256$`. Manually check the iteration count if using PBKDF2 (it's part of the stored hash string). Ensure no code uses `hashlib.sha256` etc. directly for passwords.
  </Tab>

  <Tab title="Java">
    #### Framework Context

    Spring Security defaults to `BCryptPasswordEncoder`, which is secure. The vulnerability is using `MessageDigest` (e.g., SHA-256) directly or a `PasswordEncoder` with insufficient work factor.

    #### Vulnerable Scenario 1: Direct SHA-256 Hashing

    Manually hashing passwords using `java.security.MessageDigest`.

    ```java theme={null}
    // service/UserService.java
    import java.security.MessageDigest;
    import java.security.SecureRandom;
    import java.util.Base64; // Added for encoding example
    import java.nio.charset.StandardCharsets; // Added for encoding

    public String hashPasswordSha256(String password) throws Exception {
        SecureRandom random = new SecureRandom();
        byte[] salt = new byte[16];
        random.nextBytes(salt);

        MessageDigest md = MessageDigest.getInstance("SHA-256");
        md.update(salt);
        // DANGEROUS: SHA-256 is too fast, even with a salt.
        byte[] hashedPassword = md.digest(password.getBytes(StandardCharsets.UTF_8));
        // Combine salt and hash for storage (e.g., Base64 encoded)
        return Base64.getEncoder().encodeToString(salt) + ":" + Base64.getEncoder().encodeToString(hashedPassword);
    }
    // Assume bytesToHex exists or use Base64 as above
    // private static String bytesToHex(byte[] bytes) { /* ... */ return ""; }
    ```

    #### Vulnerable Scenario 2: BCrypt with Low Strength

    Using `BCryptPasswordEncoder` but configuring it with a very low strength (work factor).

    ```java theme={null}
    // config/SecurityConfig.java
    import org.springframework.context.annotation.Bean;
    import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
    import org.springframework.security.crypto.password.PasswordEncoder;
    // ... other imports ...
    @Bean
    public PasswordEncoder passwordEncoder() {
        // DANGEROUS: Strength 4 is extremely weak and fast to compute.
        // Default is 10, recommended is 12+.
        int strength = 4;
        return new BCryptPasswordEncoder(strength);
    }
    ```

    #### Mitigation and Best Practices

    Use `BCryptPasswordEncoder` with a strength of at least 12, or use `Argon2PasswordEncoder`. Let Spring Security handle the hashing via `PasswordEncoder.encode(rawPassword)`.

    #### Secure Code Example

    ```java theme={null}
    // config/SecurityConfig.java (Secure)
    import org.springframework.context.annotation.Bean;
    import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
    import org.springframework.security.crypto.password.PasswordEncoder;
    // ... other imports ...
    @Bean
    public PasswordEncoder passwordEncoder() {
        // SECURE: Use BCrypt with adequate strength (12 or higher).
        // Or use Argon2PasswordEncoder.defaultsForSpringSecurity_v5_8();
        int strength = 12;
        return new BCryptPasswordEncoder(strength);
    }

    // service/UserService.java (Secure Usage)
    import org.springframework.beans.factory.annotation.Autowired; // Added import
    import org.springframework.security.crypto.password.PasswordEncoder; // Added import
    import org.springframework.stereotype.Service; // Added import
    // ... other imports ...
    @Service // Mark as a service for injection
    public class UserService { // Added class definition
        @Autowired // Add Autowired for injection
        private PasswordEncoder passwordEncoder;

        public String hashPasswordSecure(String rawPassword) {
            // SECURE: Delegate hashing to the configured PasswordEncoder.
            return passwordEncoder.encode(rawPassword);
        }
        // Assume method exists to check password:
        // public boolean checkPassword(String rawPassword, String encodedPassword) {
        //    return passwordEncoder.matches(rawPassword, encodedPassword);
        // }
    }
    ```

    #### Testing Strategy

    Check password hashes in the database; they should start with `$2a$12$` (BCrypt strength 12) or `$argon2id$`. Write a unit test for the `PasswordEncoder` bean to ensure it's BCrypt or Argon2 with sufficient strength.
  </Tab>

  <Tab title=".NET(C#)">
    #### Framework Context

    ASP.NET Core Identity defaults to `PBKDF2` with HMAC-SHA256 and a high iteration count (>= 10,000 in recent versions), which is acceptable. The vulnerability is using `SHA256.Create()` directly or configuring Identity with low iteration counts.

    #### Vulnerable Scenario 1: Direct SHA-256 Hashing

    Manually hashing passwords using `System.Security.Cryptography.SHA256`.

    ```csharp theme={null}
    // Services/AuthService.cs
    using System.Security.Cryptography;
    using System.Text;
    using System; // Added for Convert, Buffer

    public string HashPasswordSha256(string password, byte[] salt)
    {
        using (var sha256 = SHA256.Create())
        {
            byte[] passwordBytes = Encoding.UTF8.GetBytes(password);
            byte[] saltedPassword = new byte[salt.Length + passwordBytes.Length];
            // Combine salt and password bytes
            Buffer.BlockCopy(salt, 0, saltedPassword, 0, salt.Length);
            Buffer.BlockCopy(passwordBytes, 0, saltedPassword, salt.Length, passwordBytes.Length);


            // DANGEROUS: SHA-256 is too fast for passwords.
            byte[] hashBytes = sha256.ComputeHash(saltedPassword);
            return Convert.ToBase64String(salt) + ":" + Convert.ToBase64String(hashBytes);
        }
    }
    ```

    #### Vulnerable Scenario 2: Low Iteration Count for PBKDF2

    Configuring ASP.NET Core Identity's `PasswordHasher` with insufficient iterations.

    ```csharp theme={null}
    // Startup.cs (ConfigureServices)
    using Microsoft.AspNetCore.Identity; // Added namespace
    using Microsoft.Extensions.DependencyInjection; // Added namespace for IServiceCollection
    using Microsoft.EntityFrameworkCore; // Added for DbContext example
    // ... other using statements ...
    services.Configure<PasswordHasherOptions>(options =>
        // DANGEROUS: Iteration count of 1000 is too low.
        // Default is higher (e.g., 10000+ in .NET Core 3+, 100k+ in .NET 6+).
        options.IterationCount = 1000
    );
    // Add Identity services after configuring options
    services.AddDefaultIdentity<IdentityUser>().AddEntityFrameworkStores<ApplicationDbContext>(); // Example assuming IdentityUser, ApplicationDbContext
    ```

    #### Mitigation and Best Practices

    Use the default ASP.NET Core Identity password hasher (`PasswordHasher<TUser>`). Ensure the `PasswordHasherOptions.IterationCount` is set to a high value (OWASP recommends 600,000+ for PBKDF2-SHA256). Consider using `BCrypt.Net` or other libraries if stronger hashing is needed.

    #### Secure Code Example

    ```csharp theme={null}
    // Startup.cs (ConfigureServices - Secure Default)
    using Microsoft.AspNetCore.Identity;
    using Microsoft.EntityFrameworkCore;
    using Microsoft.Extensions.DependencyInjection;
    // ... other using statements ...
    // Assuming ApplicationDbContext and IdentityUser exist
    public void ConfigureServices(IServiceCollection services) // Example method signature
    {
        services.AddDbContext<ApplicationDbContext>(options =>
            options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"))); // Example DBContext

        // SECURE: Rely on the default high iteration count (check your .NET version's default).
        services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
            .AddEntityFrameworkStores<ApplicationDbContext>();

        // Optional: Explicitly configure for higher iterations if default isn't enough
        // services.Configure<PasswordHasherOptions>(options => options.IterationCount = 600000);
    }


    // Usage (Secure - assuming UserManager is injected)
    // In a Controller or Service:
    // private readonly UserManager<IdentityUser> _userManager;
    // public YourClass(UserManager<IdentityUser> userManager) { _userManager = userManager; }
    //
    // public async Task CreateUser(RegisterViewModel model) {
    //    var user = new IdentityUser { UserName = model.Email, Email = model.Email };
    //    // SECURE: UserManager uses the configured (secure) hasher.
    //    var result = await _userManager.CreateAsync(user, model.Password);
    // }
    ```

    #### Testing Strategy

    Check the `PasswordHash` column in `AspNetUsers`. While the format encodes parameters, verifying the iteration count requires either attempting a hash verification (which uses the embedded count) or decoding the format. Ensure no code manually uses `SHA256.Create().ComputeHash()`.
  </Tab>

  <Tab title="PHP">
    #### Framework Context

    Laravel defaults to `bcrypt` (`Hash::make`), which is secure. The vulnerability is using `hash('sha256', ...)` or `md5()` manually.

    #### Vulnerable Scenario 1: Using `hash('sha256', ...)`

    A developer manually hashes a password using the generic `hash()` function.

    ```php theme={null}
    // app/Http/Controllers/RegisterController.php
    use App\Models\User; // Added namespace
    // ... other imports ...
    protected function create(array $data)
    {
        $salt = random_bytes(16);
        // DANGEROUS: 'sha256' is too fast, even with a salt.
        $passwordHash = hash('sha256', $salt . $data['password']);

        return User::create([
            'name' => $data['name'],
            'email' => $data['email'],
            'salt' => base64_encode($salt), // Assuming salt is stored separately
            'password' => $passwordHash,
        ]);
    }
    ```

    #### Vulnerable Scenario 2: Low bcrypt Cost Factor

    Configuring Laravel's bcrypt hasher with a low cost factor (rounds).

    ```php theme={null}
    // config/hashing.php
    return [
        'default' => 'bcrypt', // Ensure bcrypt is default or explicitly chosen
        // ... other drivers ...
        'bcrypt' => [
            // DANGEROUS: 'rounds' => 4 is extremely weak.
            // Default is 10 or 12 depending on Laravel version, recommended is 12+.
            'rounds' => 4,
        ],
        // ...
    ];
    ```

    #### Mitigation and Best Practices

    Always use `Hash::make()` and `Hash::check()`. Ensure the bcrypt `rounds` in `config/hashing.php` is at least 12. Consider switching the default driver to `argon2id` if your server supports it.

    #### Secure Code Example

    ```php theme={null}
    // config/hashing.php (Secure)
    return [
        'default' => env('HASH_DRIVER', 'bcrypt'), // Default driver

        'bcrypt' => [
            'rounds' => env('BCRYPT_ROUNDS', 12), // SECURE: Use a sufficient cost factor (12+)
        ],

        'argon2id' => [
            'memory' => 65536, // 64MB (adjust based on server resources)
            'threads' => 1,    // Adjust based on server cores
            'time' => 4,       // Adjust based on desired time cost
        ],
    ];


    // app/Http/Controllers/RegisterController.php (Secure Usage)
    use Illuminate\Support\Facades\Hash;
    use App\Models\User; // Added namespace
    // ... other imports ...

    protected function create(array $data)
    {
        return User::create([
            'name' => $data['name'],
            'email' => $data['email'],
            // SECURE: Uses Laravel's configured default (bcrypt or Argon2).
            'password' => Hash::make($data['password']),
        ]);
    }
    ```

    #### Testing Strategy

    Check password hashes in the database; they should start with `$2y$12$` (bcrypt cost 12) or `$argon2id$`. Verify the `rounds` in `config/hashing.php`. Ensure no code uses `hash()`, `md5()`, or `sha1()` for passwords.
  </Tab>

  <Tab title="Node.js">
    #### Framework Context

    Node's built-in `crypto.pbkdf2` is available but requires careful parameter selection. The standard is the `bcrypt` library. Using `crypto.createHash('sha256', ...)` is the vulnerability.

    #### Vulnerable Scenario 1: Direct SHA-256 Hashing

    Hashing passwords using the built-in `crypto` module directly.

    ```javascript theme={null}
    // utils/auth.js
    const crypto = require('crypto');

    function hashPasswordSha256(password) {
      const salt = crypto.randomBytes(16);
      // DANGEROUS: SHA-256 is too fast.
      const hash = crypto.createHash('sha256').update(salt.toString('hex') + password).digest('hex');
      return salt.toString('hex') + ':' + hash; // Store salt separately
    }
    ```

    #### Vulnerable Scenario 2: Using `crypto.pbkdf2` with Low Iterations

    Using the built-in PBKDF2 but with insufficient iterations.

    ```javascript theme={null}
    // utils/auth.js
    const crypto = require('crypto');

    function hashPasswordWeakPbkdf2(password, callback) {
      const salt = crypto.randomBytes(16);
      // DANGEROUS: 1000 iterations is too low. Need 600k+ for sha512 now.
      crypto.pbkdf2(password, salt, 1000, 64, 'sha512', (err, derivedKey) => {
        if (err) return callback(err); // Pass error to callback
        // Store salt with hash
        callback(null, salt.toString('hex') + ':' + derivedKey.toString('hex'));
      });
    }
    ```

    #### Mitigation and Best Practices

    Use the `bcrypt` library (`bcrypt.hash`, `bcrypt.compare`). Ensure the salt rounds parameter is 12 or higher. Alternatively use Node's `crypto.pbkdf2` with a very high iteration count (check OWASP recommendations).

    #### Secure Code Example

    ```javascript theme={null}
    // utils/auth.js (Secure with bcrypt)
    const bcrypt = require('bcrypt');
    const saltRounds = 12; // SECURE: Recommended salt rounds

    async function hashPasswordSecure(password) {
      // SECURE: Uses bcrypt async with salt rounds (salt generated automatically)
      const hash = await bcrypt.hash(password, saltRounds);
      return hash; // bcrypt hash includes salt and cost factor
    }

    async function comparePassword(password, hash) {
      // Handles parsing salt/cost from hash automatically
      return await bcrypt.compare(password, hash);
    }

    // utils/auth.js (Secure with crypto.pbkdf2)
    const crypto = require('crypto');
    const util = require('util'); // For promisify
    const pbkdf2Async = util.promisify(crypto.pbkdf2);

    async function hashPasswordSecurePbkdf2(password) {
        const salt = crypto.randomBytes(16);
        const iterations = 600000; // SECURE: Use high iteration count (check current OWASP)
        const keylen = 64;
        const digest = 'sha512';
        const derivedKey = await pbkdf2Async(password, salt, iterations, keylen, digest);
        // Store salt, iterations, digest, keylen with the hash
        return `pbkdf2_${digest}$${iterations}$${salt.toString('hex')}$${derivedKey.toString('hex')}`;
    }
    ```

    #### Testing Strategy

    Check password hashes in the database; they should start with `$2b$12$`. Ensure no code uses `crypto.createHash` directly for passwords. If using `crypto.pbkdf2`, verify the iteration count is high (e.g., > 600,000).
  </Tab>

  <Tab title="Ruby">
    #### Framework Context

    Rails' `has_secure_password` uses `BCrypt::Password.create`, which is secure. The vulnerability is using `Digest::SHA256.hexdigest` manually.

    #### Vulnerable Scenario 1: Direct SHA-256 Hashing

    Manually hashing a password using `Digest::SHA256`.

    ```ruby theme={null}
    # app/models/legacy_user.rb
    require 'digest'
    require 'securerandom'

    class LegacyUser < ApplicationRecord # Assume base class exists
      # Example manual hashing (vulnerable)
      def password=(new_password)
        self.salt = SecureRandom.hex(16)
        # DANGEROUS: SHA256 is too fast.
        self.password_digest = Digest::SHA256.hexdigest(self.salt + new_password)
      end
      # Need a corresponding check_password method too
    end
    ```

    #### Vulnerable Scenario 2: Low Cost Factor for bcrypt

    Manually using `BCrypt::Password.create` with a low cost factor.

    ```ruby theme={null}
    # lib/custom_hasher.rb
    require 'bcrypt'

    module CustomHasher
      def self.hash_password_weak_bcrypt(password)
        # DANGEROUS: Cost factor 4 is extremely weak.
        # Default is usually 12.
        BCrypt::Password.create(password, cost: 4)
      end
    end
    ```

    #### Mitigation and Best Practices

    Use `has_secure_password` in your Active Record model. This leverages the `bcrypt` gem, which uses a secure default cost factor. If hashing manually, use `BCrypt::Password.create(password)` which uses the default cost.

    #### Secure Code Example

    ```ruby theme={null}
    # app/models/user.rb (Secure)
    class User < ApplicationRecord
      # SECURE: Uses BCrypt::Password with default cost factor (>= 12).
      # Requires 'bcrypt' gem in Gemfile.
      has_secure_password
    end

    # lib/custom_hasher.rb (Secure Manual Hashing)
    require 'bcrypt'

    module CustomHasher
      def self.hash_password_secure(password)
        # SECURE: Uses default cost factor (>= 12)
        BCrypt::Password.create(password)
      end

      def self.verify_password(password, hash)
         # Check if hash is valid BCrypt hash before attempting compare
         return false unless BCrypt::Password.valid_hash?(hash)
         bcrypt_hash = BCrypt::Password.new(hash)
         # SECURE: Comparison is timing-attack resistant
         bcrypt_hash == password # Compares password against embedded salt/hash
      end
    end
    ```

    #### Testing Strategy

    Check the `password_digest` column in your database; hashes should start with `$2a$12$` (bcrypt cost 12). Ensure no code manually uses `Digest::SHA256` etc. for passwords. If using `BCrypt::Password.create` manually, verify the cost factor isn't explicitly set low.
  </Tab>
</Tabs>
