VNX-257: Storing Passwords in a Recoverable Format
Reports a password passed as the data of a reversible transform (base64, hex, rot13, Fernet/AES/cipher encryption, CryptoJS, openssl_encrypt, AEAD Seal) and any decrypt_password/decryptPassword function.
| Field | Value |
|---|---|
| Rule ID | VNX-257 |
| Severity | High |
| SARIF level | error |
| Kind | sast |
| Languages | Go, Java, JavaScript / TypeScript, PHP, Python, Ruby |
| CWE | CWE-257 |
| CAPEC | CAPEC-66 |
| ATT&CK | T1190 |
| CWSS vector | CWSS:1.0/TI:H/AP:A/AL:A/IC:N/FC:LT/RP:N/RL:A/AV:I/AS:N/IN:A/SC:A/BI:H/DI:H/EX:H/EC:N/P:H |
| Tags | password-recovery reversible cwe-257 |
| Source | internal/sast/rules/vnx-257.rego |
Overview
An encoded or encrypted password can be turned back into the password by anyone holding the stored value, or the stored value and the key. Passwords should be hashed with a slow, salted function, and forgotten passwords reset rather than recovered. This maps to CWE-257: Storing Passwords in a Recoverable Format.
What Gets Flagged
The first (data) argument of a reversible transform is a password-named expression (password, newPassword, user.password, $_POST['password'], getPassword(), optionally wrapped in .encode(), .getBytes() or []byte()):
- Python
base64.b64encodeand friends,binascii.hexlify,codecs.encode, any.encrypt(...); - JavaScript
btoa,Buffer.from(...).toString('base64'|'hex'),CryptoJS.AES/DES/TripleDES/RC4/Rabbit.encrypt,cipher.update; - Java
Base64.getEncoder().encode/encodeToString,cipher.doFinal,.encrypt; - PHP
base64_encode,openssl_encrypt,str_rot13; - Ruby
Base64.encode64/strict_encode64/urlsafe_encode64; - Go
base64.*.EncodeToString,hex.EncodeToString, an AEADSealof the password.
A function named decrypt_password, decryptPassword or DecryptPassword, defined or called, is also reported.
user.password = base64.b64encode(password.encode()).decode() # flagged
token = Fernet(settings.KEY).encrypt(password.encode()) # flagged
const enc = CryptoJS.AES.encrypt(password, process.env.KEY).toString(); // flagged
Not flagged: HTTP Basic credentials (a line that builds user:password or names Basic), a password used as the key or as input to a key-derivation function, encoding of a hash, salt or digest, names that only contain the word (passwordFieldId), comments, strings and test code.
Remediation
import bcrypt
user.password_hash = bcrypt.hashpw(password.encode(), bcrypt.gensalt())
Replace any “send me my password” flow with a time-limited reset link.
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-257
// nosec vnx-257
Or record an ignore rule: vulnetix ignore add --rule vnx-257 --reason "...". See Suppressing findings.