> ## 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.

# Use of a One-Way Hash without a Salt

> Mitigation for hashing passwords without a unique salt, making them vulnerable to rainbow table attacks.

## Overview

This vulnerability occurs when an application hashes passwords using a one-way cryptographic hash function (like SHA-256) but fails to include a unique, random **salt** for each user. A salt is a random value added to the password *before* hashing. Without a salt, identical passwords will produce identical hashes. Attackers can precompute hashes for common passwords (a "rainbow table") and quickly find matches if they obtain the hash database. 🌈💥

## Business Impact

Hashing without a salt makes password cracking significantly faster and easier for attackers who steal the hash database. Even if a strong hashing algorithm is used, the lack of salt allows attackers to crack multiple identical passwords simultaneously using precomputed tables. This leads to mass account compromise.

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

## Framework-Specific Analysis and Remediation

Modern password hashing functions provided by frameworks (bcrypt, Argon2, PBKDF2 implementations) **automatically generate and manage unique salts** for each password. This vulnerability primarily occurs when developers implement password hashing manually using basic hash functions (like SHA-256) and either forget the salt entirely or use a static, non-unique salt. The fix is to **always use the framework's recommended password hashing functions**, which handle salting correctly.

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

    Using `hashlib.sha256(password.encode()).hexdigest()` directly without adding a salt.

    #### Vulnerable Scenario 1: Direct Hashing (No Salt)

    A custom user model hashes the password directly without any salt.

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

    def set_password(self, raw_password):
        # DANGEROUS: No salt is used. Two users with the same password
        # will have the same hash, making rainbow tables effective.
        self.password_hash = hashlib.sha256(raw_password.encode()).hexdigest()
        
    def check_password(self, raw_password):
        check_hash = hashlib.sha256(raw_password.encode()).hexdigest()
        return check_hash == self.password_hash
    ```

    #### Vulnerable Scenario 2: Static Salt

    A developer adds a salt, but it's a hardcoded, static value used for all users.

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

    # DANGEROUS: Using a single, static salt is almost as bad as no salt.
    # Precomputation is still possible for this specific salt.
    STATIC_SALT = b"my_super_secret_static_salt" 

    def set_password(self, raw_password):
        salted_input = STATIC_SALT + raw_password.encode()
        self.password_hash = hashlib.sha256(salted_input).hexdigest()
        
    def check_password(self, raw_password):
        salted_input = STATIC_SALT + raw_password.encode()
        check_hash = hashlib.sha256(salted_input).hexdigest()
        return check_hash == self.password_hash
    ```

    #### Mitigation and Best Practices

    Use Django's built-in password management (`user.set_password()`, `user.check_password()`) which automatically handles unique salting and uses strong algorithms (like PBKDF2, bcrypt, Argon2).

    #### Secure Code Example

    ```python theme={null}
    # models.py (Using Django's AbstractUser or similar)
    from django.contrib.auth.models import User # Or AbstractUser

    # SECURE: Django's built-in methods handle salting correctly.
    user = User.objects.get(username='test')
    user.set_password('newS3cureP@ssw0rd') 
    user.save()

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

    #### Testing Strategy

    Inspect password hashes in the database. Hashes generated by Django's secure hashers (PBKDF2, bcrypt, Argon2) embed the salt within the hash string itself. You should see different hash values even for users with the same password. Ensure no code directly uses `hashlib.shaXXX()` without a unique, per-user salt.
  </Tab>

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

    Using `java.security.MessageDigest` directly without prepending a unique, per-user salt. Spring Security's `PasswordEncoder` implementations (like `BCryptPasswordEncoder`) handle salting internally.

    #### Vulnerable Scenario 1: Direct Hashing (No Salt)

    Manually hashing passwords using `MessageDigest` without adding a salt.

    ```java theme={null}
    // service/UserService.java
    import java.security.MessageDigest;

    public String hashPasswordNoSalt(String password) throws Exception {
        MessageDigest md = MessageDigest.getInstance("SHA-256");
        // DANGEROUS: No salt is added before hashing.
        byte[] hashedPassword = md.digest(password.getBytes("UTF-8"));
        return bytesToHex(hashedPassword); 
    }
    ```

    #### Vulnerable Scenario 2: System-Wide Static Salt

    Using a single `salt` value loaded from configuration for all passwords.

    ```java theme={null}
    // service/UserService.java
    import java.security.MessageDigest;

    // Injected from config or hardcoded
    private static final byte[] STATIC_SALT = "load_from_config".getBytes(); 

    public String hashPasswordStaticSalt(String password) throws Exception {
        MessageDigest md = MessageDigest.getInstance("SHA-256");
        // DANGEROUS: Using the same salt for every user.
        md.update(STATIC_SALT); 
        byte[] hashedPassword = md.digest(password.getBytes("UTF-8"));
        return bytesToHex(hashedPassword); 
        // Note: The salt is not even stored with the hash here!
    }
    ```

    #### Mitigation and Best Practices

    Use Spring Security's `PasswordEncoder` interface with a secure implementation like `BCryptPasswordEncoder` or `Argon2PasswordEncoder`. These generate and embed a unique salt within the resulting hash string automatically.

    #### Secure Code Example

    ```java theme={null}
    // config/SecurityConfig.java (Secure)
    import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
    import org.springframework.security.crypto.password.PasswordEncoder;
    // ...
    @Bean
    public PasswordEncoder passwordEncoder() {
        // SECURE: BCryptPasswordEncoder generates and manages salts internally.
        return new BCryptPasswordEncoder(); 
    }

    // service/UserService.java (Secure Usage)
    @Autowired
    private PasswordEncoder passwordEncoder;

    public String hashPasswordSecure(String rawPassword) {
        // SECURE: encode() handles salting and hashing.
        return passwordEncoder.encode(rawPassword);
    }

    public boolean checkPassword(String rawPassword, String encodedPassword) {
        // SECURE: matches() extracts the salt from encodedPassword and verifies.
        return passwordEncoder.matches(rawPassword, encodedPassword);
    }
    ```

    #### Testing Strategy

    Check password hashes in the database; bcrypt hashes start with `$2a$` (or similar) and include the salt and cost factor. Ensure `MessageDigest.getInstance("SHA-...")` is not used directly for password hashing without proper, unique salting.
  </Tab>

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

    ASP.NET Core Identity's `PasswordHasher` correctly generates and uses unique salts per password. The vulnerability occurs when manually hashing using `SHA256.Create()` without a salt.

    #### Vulnerable Scenario 1: Direct Hashing (No Salt)

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

    ```csharp theme={null}
    // Services/AuthService.cs
    using System.Security.Cryptography;
    using System.Text;

    public string HashPasswordSha256NoSalt(string password)
    {
        using (var sha256 = SHA256.Create())
        {
            byte[] passwordBytes = Encoding.UTF8.GetBytes(password);
            // DANGEROUS: No salt is included in the hash input.
            byte[] hashBytes = sha256.ComputeHash(passwordBytes);
            return Convert.ToBase64String(hashBytes); // Salt is not stored either.
        }
    }
    ```

    #### Vulnerable Scenario 2: Global Static Salt

    Using a single salt value retrieved from configuration for all users.

    ```csharp theme={null}
    // Services/AuthService.cs
    using System.Security.Cryptography;
    using System.Text;

    private readonly byte[] _staticSalt; // Injected from IConfiguration

    public AuthService(IConfiguration config) {
        _staticSalt = Convert.FromBase64String(config["Security:StaticPasswordSalt"]);
    }

    public string HashPasswordStaticSalt(string password)
    {
        using (var sha256 = SHA256.Create())
        {
            byte[] passwordBytes = Encoding.UTF8.GetBytes(password);
            byte[] saltedPassword = new byte[_staticSalt.Length + passwordBytes.Length];
            // ... combine _staticSalt and passwordBytes ...
            
            // DANGEROUS: Same salt used for everyone.
            byte[] hashBytes = sha256.ComputeHash(saltedPassword);
            return Convert.ToBase64String(hashBytes); // Salt is not stored with hash.
        }
    }
    ```

    #### Mitigation and Best Practices

    Use the built-in ASP.NET Core Identity services (`UserManager<TUser>`, `SignInManager<TUser>`). The `PasswordHasher<TUser>` automatically handles generation, storage (embedding the salt in the hash string), and verification.

    #### Secure Code Example

    ```csharp theme={null}
    // Using ASP.NET Core Identity (Secure)
    public class AccountController : Controller
    {
        private readonly UserManager<IdentityUser> _userManager;

        public AccountController(UserManager<IdentityUser> userManager)
        {
            _userManager = userManager;
        }

        public async Task<IActionResult> Register(RegisterViewModel model)
        {
            var user = new IdentityUser { /* ... */ };
            // SECURE: CreateAsync uses the configured PasswordHasher,
            // which handles salting correctly.
            var result = await _userManager.CreateAsync(user, model.Password); 
            // ...
        }
        
        public async Task<bool> CheckPasswordSignIn(LoginViewModel model)
        {
           var user = await _userManager.FindByEmailAsync(model.Email);
           // SECURE: CheckPasswordAsync handles salt extraction and verification.
           return await _userManager.CheckPasswordAsync(user, model.Password);
        }
    }
    ```

    #### Testing Strategy

    Check the `PasswordHash` column in `AspNetUsers`. Hashes generated by Identity include salt and iteration count information, usually Base64 encoded. Verify that different users with the same password have different hashes. Ensure `SHA256.Create().ComputeHash()` is not used directly for passwords.
  </Tab>

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

    Laravel's `Hash::make()` (using bcrypt or Argon2) handles salting automatically and securely. The vulnerability is using `hash('sha256', ...)` or `md5()` without a unique salt.

    #### Vulnerable Scenario 1: Using `hash()` without Salt

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

    ```php theme={null}
    // app/Http/Controllers/RegisterController.php
    protected function create(array $data)
    {
        // DANGEROUS: No salt is used. Identical passwords get identical hashes.
        $passwordHash = hash('sha256', $data['password']);
        
        return User::create([
            'name' => $data['name'],
            'email' => $data['email'],
            'password' => $passwordHash,
        ]);
    }
    ```

    #### Vulnerable Scenario 2: Using `crypt()` with Weak Salt/Method

    Using the older `crypt()` function with a weak method or a predictable/static salt.

    ```php theme={null}
    // app/Utils/LegacyAuth.php
    function hashPasswordCryptWeak($password) {
        // DANGEROUS: Using CRYPT_MD5 method (prefix $1$) is weak.
        // DANGEROUS: Using a static salt 'staticsalt' makes it easily breakable.
        return crypt($password, '$1$staticsalt$'); 
    }
    ```

    #### Mitigation and Best Practices

    Always use `Hash::make()` and `Hash::check()`. These functions use bcrypt or Argon2id by default, which include robust, automatic salting. Avoid `md5()`, `sha1()`, `hash()`, and `crypt()` for password hashing.

    #### Secure Code Example

    ```php theme={null}
    // app/Http/Controllers/RegisterController.php (Secure Usage)
    use Illuminate\Support\Facades\Hash;

    protected function create(array $data)
    {
        return User::create([
            'name' => $data['name'],
            'email' => $data['email'],
            // SECURE: Hash::make() uses bcrypt/Argon2id and handles salting.
            'password' => Hash::make($data['password']), 
        ]);
    }

    // Checking password (Secure)
    if (Hash::check($attemptedPassword, $user->password)) {
        // Password is correct
    }
    ```

    #### Testing Strategy

    Check password hashes in the database; they should start with `$2y$` (bcrypt) or `$argon2id$` (Argon2id). These formats include the salt. Verify that two users created with the same password have different hash strings. Ensure `hash()`, `md5()`, `sha1()`, `crypt()` are not used for password hashing.
  </Tab>

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

    Node's built-in `crypto` module requires manual salt handling. The standard `bcrypt` library handles salting automatically. Using `crypto.createHash(...)` without a unique salt is the vulnerability.

    #### Vulnerable Scenario 1: Direct Hashing (No Salt)

    Hashing passwords using the built-in `crypto` module without a salt.

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

    function hashPasswordSha256NoSalt(password) {
      // DANGEROUS: No salt used.
      const hash = crypto.createHash('sha256').update(password).digest('hex');
      return hash;
    }
    ```

    #### Vulnerable Scenario 2: Static Salt

    Using a single, hardcoded salt for all password hashes.

    ```javascript theme={null}
    // utils/auth.js
    const crypto = require('crypto');
    const STATIC_SALT = 'a-fixed-salt-value'; // DANGEROUS

    function hashPasswordStaticSalt(password) {
      const hash = crypto.createHash('sha256').update(STATIC_SALT + password).digest('hex');
      // DANGEROUS: Same salt for everyone, and not stored with hash.
      return hash; 
    }
    ```

    #### Mitigation and Best Practices

    Use the `bcrypt` library (`bcrypt.hash`, `bcrypt.compare`). It automatically generates a unique salt for each hash and embeds it in the resulting string.

    #### Secure Code Example

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

    async function hashPasswordSecure(password) {
      // SECURE: bcrypt.hash generates a unique salt automatically.
      const hash = await bcrypt.hash(password, saltRounds);
      // The returned hash string contains the salt, cost factor, and hash.
      return hash; 
    }

    async function comparePassword(password, hash) {
      // SECURE: bcrypt.compare extracts the salt from the hash string.
      return await bcrypt.compare(password, hash);
    }
    ```

    #### Testing Strategy

    Check password hashes in the database; they should start with `$2b$12$` (bcrypt, cost 12). Verify that users with the same password have different hash strings. Ensure `crypto.createHash` is not used directly for password hashing.
  </Tab>

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

    Rails' `has_secure_password` uses `BCrypt::Password.create`, which handles salting automatically. The vulnerability is using `Digest::SHA256.hexdigest` without a unique salt.

    #### Vulnerable Scenario 1: Direct Hashing (No Salt)

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

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

    def password=(new_password)
      # DANGEROUS: No salt is used.
      self.password_digest = Digest::SHA256.hexdigest(new_password)
    end
    ```

    #### Vulnerable Scenario 2: Static Salt

    Using a hardcoded, application-wide salt.

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

    # DANGEROUS: Single salt for all users.
    STATIC_SALT = "my-app-wide-secret-salt" 

    def password=(new_password)
      salted_input = STATIC_SALT + new_password
      self.password_digest = Digest::SHA256.hexdigest(salted_input)
    end
    ```

    #### Mitigation and Best Practices

    Use `has_secure_password` in your Active Record model. This leverages the `bcrypt` gem, which generates and stores a unique salt per password automatically.

    #### Secure Code Example

    ```ruby theme={null}
    # app/models/user.rb (Secure)
    class User < ApplicationRecord
      # SECURE: Uses BCrypt::Password which handles salting automatically.
      has_secure_password 
    end

    # Usage:
    # user = User.new(password: 'secret')
    # user.save
    # user.authenticate('secret') # => user object if correct, false otherwise
    ```

    #### Testing Strategy

    Check the `password_digest` column in your database; hashes should start with `$2a$`. Verify that different users with the same password have different hash strings. Ensure `Digest::SHA256` or similar are not used directly for password hashing.
  </Tab>
</Tabs>
