Safeguard
Application Security•

CWE-328 Use of Weak Hash: Why MD5 and SHA-1 Fail and What to Use

Weak hashes allow preimage, second preimage and collision attacks. CWE-328 explained, with a clear path to adaptive hashing for passwords.

Safeguard Security Team
3 min read

CWE-328, Use of Weak Hash, applies when a product uses a hash algorithm whose output does not meet security expectations. An attacker can work out the original input, find another input with the same hash, or find multiple inputs that collide.

Password storage, file integrity checks, signatures and request authentication all rely on hashes, so any of them can be affected.

How it happens

A hash function maps data of any size to a fixed size digest. MITRE lists the basic properties: it is not invertible and it is deterministic. A cryptographic hash must also stop a malicious actor from exploiting the function. The three attacks named in the record are:

  • Preimage: determine the original input from the hash.
  • Second preimage: find a different input producing the same hash.
  • Birthday: find multiple inputs that evaluate to the same hash.

Weak hashes fail one or more of these. Fast hashes fail in another way for passwords: they let attackers test guesses at enormous speed.

# Vulnerable: fast, general purpose hash for passwords
stored = hashlib.md5(password.encode()).hexdigest()

Observed examples include CVE-2005-4900, where SHA-1 was not collision resistant, CVE-2022-30320, a controller using an insecure hash for passwords, and CVE-2020-25685, a DNS product using a weak hash (CRC32 or SHA-1) of the query name, allowing an attacker to forge responses by computing the hash.

What an attacker gains

The record lists bypassing a protection mechanism. For stored passwords that means recovering them from a leaked database. For integrity checks it means substituting content that passes verification. For the DNS example it means forging responses.

How to find it

Static analysis can find some instances. Search code and configuration for md5, sha1 and crc32 used in a security role. Not every use is a flaw, since a checksum for catching accidental corruption is a different job, but each use should have a stated reason.

How to fix it

MITRE's high effectiveness mitigation is an adaptive hash function: one whose computational effort can be configured, through iteration count ("stretching") or memory use. Some perform salting automatically. These raise the cost of brute force substantially compared with intentionally fast functions such as MD5, and rainbow table attacks become infeasible. Because computing power keeps getting cheaper, you can raise the workload later without replacing the algorithm. MITRE names bcrypt, scrypt and PBKDF2 as functions with these properties, and notes there is active debate about which is most effective, though all are stronger than a salt on a cheap hash.

One caution from the record: these functions cost CPU and memory, so tune them to avoid creating a denial of service.

# Fixed: salted, adaptive hash
import bcrypt
stored = bcrypt.hashpw(password.encode(), bcrypt.gensalt(rounds=12))
ok = bcrypt.checkpw(candidate.encode(), stored)

For integrity and signatures, move to a current member of the SHA-2 family or newer, and verify with a maintained library.

Quick check

Is salting an MD5 password hash enough?

No. MITRE says adaptive functions are stronger than using salts with hashes that have very little computing overhead.

Can I raise the work factor later?

Yes. That is one advantage of adaptive hashing. Rehash on next login with the new setting.

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.