Skip to content

VNX-759: Password hashed with a one-way hash and no salt

A password is passed straight into a fast general-purpose digest with no salt, so equal passwords get equal hashes and a precomputed table recovers them. Reported, when the hashed argument names a password (password, passwd, passphrase, pwd, pw, $pass, $pass_new, getPassword(), a subscript or .get() keyed by a password field such as form["new_password"] or request.json.get("password")) or a variable assigned from one a few lines above: Python hashlib.md5/sha1/sha224/sha256/sha384/sha512/sha3_*/blake2*/new(...) (also from-imported) and <hashlib object>.update(password); Java MessageDigest <md>.digest(password...)/<md>.update(password...) and MessageDigest.getInstance(...).digest(password...), commons-codec DigestUtils.md5Hex/sha1Hex/sha256Hex/... and Guava Hashing.md5()/sha1()/sha256()/sha512().hashString/hashBytes; PHP md5()/sha1()/crc32()/hash('<alg>', ...); Node crypto.createHash(...).update(password) on one line, as a chained call split over lines, or through a variable (const h = crypto.createHash(...); h.update(password)). A call argument split onto the next line is followed. Not reported: an argument or a neighbouring update() that mixes in a salt or pepper, password KDFs (pbkdf2_hmac, scrypt, bcrypt, argon2, PBEKeySpec, password_hash, scryptSync), HMAC, digests of tokens, nonces, password-reset values, file contents or other non-password data, string literals and comments. Weak iteration counts and broken algorithms are other rules (VNX-CRYPTO-007, VNX-327). A password passed straight into a fast digest call (md5($pass), hashlib.sha256(password.encode()), createHash('sha256').update(password), DigestUtils.sha256Hex(password)) is reported by VNX-CRYPTO-007, a password hashed with a fast hash, whose fix (a salted password KDF) is this rule's fix too; this rule keeps the shapes that rule does not read, such as a password reached through a variable, a subscript or a hasher fed on a later line.

Updated 02 Nov 2026Checked against cli@de9b52a
Field Value
Rule ID VNX-759
Severity High
SARIF level error
Kind sast
Languages Java, Python, PHP, JavaScript / TypeScript
CWE CWE-759
CAPEC CAPEC-66
ATT&CK T1190
CWSS vector CWSS:1.0/TI:H/AP:RU/AL:S/IC:N/FC:LT/RP:RU/RL:S/AV:L/AS:N/IN:A/SC:A/BI:H/DI:H/EX:H/EC:N/P:H
Tags password-hash unsalted cwe-759
Source internal/sast/rules/vnx-759.rego

Overview

VNX-759 maps to CWE-759: Use of a One-Way Hash without a Salt. It reports a fast general-purpose digest whose input is a password (an argument named password, passwd, pwd, $pass, getPassword() and similar, or a variable assigned from one a few lines above) with no salt or pepper mixed into the same digest: Python hashlib.* and h.update(password), Java MessageDigest.digest/update, DigestUtils.*Hex and Guava Hashing.*().hashString, PHP md5(), sha1(), crc32() and hash('<alg>', ...), and Node crypto.createHash(...).update(password).

It does not report salted digests, password KDFs (pbkdf2_hmac, scrypt, bcrypt, Argon2, PBEKeySpec, password_hash), HMAC, or digests of tokens, nonces, reset values and file contents.

A password passed straight into a fast digest call (md5($pass), hashlib.sha256(password.encode()), createHash('sha256').update(password)) is reported by VNX-CRYPTO-007, whose fix is the same salted password KDF, so the line is reported once. This rule keeps what that rule does not read: a password reached through a variable assigned a few lines above, a request field, or a hasher fed on a later line.

Why This Matters

Without a salt, two users with the same password get the same hash, and a single precomputed (rainbow) table reverses every hash in a leaked table at once. Fast digests also let an attacker try billions of guesses per second.

What Gets Flagged

cred = request.form["new_password"]
user.password_digest = hashlib.sha256(cred.encode()).hexdigest()       # flagged
MessageDigest md = MessageDigest.getInstance("SHA-256");
return md.digest(password.getBytes(StandardCharsets.UTF_8));            // flagged
const h = crypto.createHash('sha256');
h.update(password);                                                     // flagged

Reported by VNX-CRYPTO-007 instead: $pass = md5($pass);, hashlib.sha256(password.encode()), crypto.createHash('sha256').update(password).

Remediation

  1. Hash passwords with a password KDF that salts for you: bcrypt, scrypt, Argon2id or PBKDF2 with a high iteration count (password_hash() in PHP, BCryptPasswordEncoder in Spring).
  2. Migrate existing hashes on the next successful login.

References

Suppressing this rule

Put a nosec comment on the line, or on line 1 to cover the whole file. Use the comment marker of the file’s language (#, //, -- or ;):

# nosec vnx-759
// nosec vnx-759

Or record an ignore rule: vulnetix ignore add --rule vnx-759 --reason "...". See Suppressing findings.

Edit this pageSources: cli/internal/sast/rules/vnx-759.rego, cli/internal/sast/docs/vnx-759.md
esc
↑ ↓ move↵ openSearches in your browser. A key like autonomy.max or a code like threshold_required jumps straight to it.