Weakness reference
CWE-760

Use of a One-Way Hash with a Predictable Salt

This weakness occurs when a system hashes sensitive data like passwords with a salt, but the salt itself is predictable or reused across multiple users. A salt…

01Summary

This weakness occurs when a system hashes sensitive data (like passwords) with a salt, but the salt itself is predictable or reused across multiple users. A salt is meant to make precomputed hash tables (rainbow tables) ineffective by ensuring each hash is unique; a predictable salt defeats this purpose, allowing attackers to precompute hashes for common passwords and match them against stolen hashes.

02How It Happens

Salts are random values added to input before hashing to ensure that identical passwords produce different hashes. However, if the salt is derived from predictable data—such as a username, a fixed constant, a timestamp, or a low-entropy random source—an attacker can precompute hashes for common passwords using that same salt. When the attacker obtains a password hash database, they can match hashes directly or use precomputed tables, bypassing the security benefit the salt was supposed to provide.

The root cause is usually one of three patterns: using a constant salt across all users, deriving the salt from public or semi-public information, or using a weak random number generator that produces a limited set of possible salts.

03Real-World Impact

If an attacker gains access to a password hash database protected by a predictable salt, they can crack passwords far more efficiently than if each hash had a unique, random salt. This can lead to account takeover, unauthorized access to user data, and lateral movement within a system. The impact is especially severe if the same salt is reused across multiple systems or if the salt is derived from information already public (like a username).

04Vulnerable & Fixed Patterns

Vulnerable pattern
import hashlib

username = "alice"
password = "mypassword"

# Predictable salt: derived from username
salt = username.encode()

# Hash with predictable salt
hashed = hashlib.pbkdf2_hmac('sha256', password.encode(), salt, 100000)
print(hashed.hex())

Why it's vulnerable:
The salt is derived from the username, which is public information. An attacker who knows the username can precompute hashes for common passwords using that same salt, making the hash table attack nearly as effective as if no salt were used.

Fixed pattern
import hashlib
import secrets

password = "mypassword"

# Generate a cryptographically random salt
salt = secrets.token_bytes(32)

# Hash with random salt
hashed = hashlib.pbkdf2_hmac('sha256', password.encode(), salt, 100000)

# Store both salt and hash (salt does not need to be secret)
stored = salt + hashed
print(stored.hex())
Vulnerable pattern
<?php
$username = "alice";
$password = "mypassword";

// Predictable salt: fixed constant
$salt = "fixed_salt_value";

// Hash with predictable salt
$hashed = hash('sha256', $salt . $password);
echo $hashed;
?>

Why it's vulnerable:
The salt is a hardcoded constant reused for every user. An attacker can precompute hashes for all common passwords with this one salt and match them against any stolen hash database.

Fixed pattern
<?php
$password = "mypassword";

// Use password_hash() which handles salt generation and hashing securely
$hashed = password_hash($password, PASSWORD_BCRYPT);

// Verify during login
if (password_verify($password, $hashed)) {
    echo "Password correct";
}
?>

05Prevention Checklist

Use a cryptographically secure random number generator (e.g., secrets in Python, random_bytes() in PHP) to generate a unique salt for each password.
Ensure each salt is at least 16 bytes (128 bits) of entropy; 32 bytes is recommended.
Never derive a salt from predictable data such as usernames, email addresses, timestamps, or sequential IDs.
Use a modern password hashing algorithm (bcrypt, scrypt, Argon2) that handles salt generation automatically rather than implementing your own.
Store the salt alongside the hash; the salt does not need to be secret, only unique and random.
Audit existing password storage to identify any use of constant, reused, or predictable salts and migrate to secure hashing.

06Signs You May Already Be Affected

Review your password storage code for hardcoded salt values, salts derived from usernames or other public data, or use of weak random functions. If you find evidence of constant or predictable salts in your codebase, assume password hashes may be at elevated risk of cracking and consider forcing a password reset for all users.