Safeguard
Application Security•

CWE-760 One-Way Hash with a Predictable Salt: Why It Fails

A constant or guessable salt gives you none of the protection a salt is meant to provide. See the pattern, the real examples, and the stronger fix.

Safeguard Security Team
4 min read

CWE-760 covers password hashing that uses a salt an attacker can predict. The product hashes an input that should not be reversible, such as a password, but the salt it mixes in is predictable.

Any application that stores credentials is a candidate, especially older ones with home grown hashing code.

How it happens

A salt only helps if it is unique per password and unpredictable. Developers break that in a few common ways: a constant string in the source, the username, or a value derived from something the attacker can see.

The record lists several cases. CVE-2008-4905 is blogging software that used a hard coded salt. CVE-2002-1657 is a database server that used the username as the salt. CVE-2001-0967 is a server that used a constant salt. CVE-2005-0408 is a chain where a product generated predictable MD5 hashes from a constant value combined with the username, allowing authentication bypass.

# Vulnerable: the same salt for every user, and a fast hash
import hashlib
def store(password):
    return hashlib.md5(("fixedsalt" + password).encode()).hexdigest()

What an attacker gains

The consequence is a bypassed protection mechanism. A predictable salt lets the attacker precompute hashes with dictionary techniques such as rainbow tables, which effectively disables the protection a good salt would give.

How to find it

MITRE says static analysis can find some instances. Search the code for hash calls on passwords and read what is mixed in. A string literal, a username or an incrementing counter in that position is the finding.

How to fix it

MITRE's first recommendation is bigger than the salt. Use an adaptive hash function that can be tuned to need more computation or memory, such as bcrypt, scrypt or PBKDF2. These perform salting for you, and they make brute force far more expensive than a fast hash like MD5.

# Fix: scrypt with a random per-user salt, both stored
import hashlib, os
salt = os.urandom(16)
digest = hashlib.scrypt(password.encode(), salt=salt, n=2**14, r=8, p=1)

If you cannot use an adaptive function, generate a new random salt for every password with a strong random number generator, store it beside the hash, and never reuse one. MITRE rates that fallback as limited, and cautions that these functions can affect performance, so budget for it.

Why developers fall into it

Most predictable salts come from a half remembered rule. Someone learned that passwords need a salt, and added one, but the goal was to satisfy the rule rather than to get the property. A constant in the source file is the easiest thing to type. The username is convenient because it is already at hand and it is unique per account, so it feels random enough.

The test is simple: could an attacker who has never seen your database work out the salt for a given account? If yes, the attacker can precompute against it. A hard coded value is known to anyone who reads the source or the binary. A username is known to anyone who knows the account.

Migrating is the awkward part. You cannot rehash stored hashes, because you do not have the passwords. The usual approach is to wrap the old hash inside the new function, or rehash at the next successful login, and to flag accounts that have not logged in for forced reset.

Quick check

Is using the username as a salt acceptable?

No. It is predictable, and identical passwords for the same username across systems hash identically. Use a random value per password.

Is a good salt enough on its own?

MITRE says no. Against a fast hash, offline cracking is still effective. Pair the salt with an adaptive function.

Sources

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.