Overview
This vulnerability involves storing user passwords in a format that allows the original password to be retrieved by the application or an attacker who gains access to the storage. This includes storing passwords in plaintext (seeCWE-312) or using reversible encryption (like AES, DES, two-way encryption) instead of a strong, salted, one-way hash function (like bcrypt, Argon2, PBKDF2, scrypt). 🔑🔄
Business Impact
Storing passwords recoverably is extremely dangerous. If the storage or the encryption key is compromised:- Complete Password Exposure: Attackers gain access to the users’ actual passwords, not just hashes.
- Credential Stuffing: Since users often reuse passwords, attackers can use the exposed passwords to compromise accounts on other unrelated websites.
- Compliance Violations: Storing passwords reversibly violates numerous security standards and regulations (e.g., PCI-DSS explicitly forbids it).
- Irreversible Reputational Damage: A breach involving plaintext or easily decrypted passwords causes a massive loss of user trust.
Reference Details
CWE ID: CWE-257
OWASP Top 10 (2021): A04:2021 - Insecure Design (Failure to use secure password storage) & A02:2021 - Cryptographic Failures
Severity: Critical
Framework-Specific Analysis and Remediation
This is a critical design flaw. The only secure way to store passwords is using a strong, adaptive, salted, one-way hash function. Frameworks provide tools for this (often by default), but developers might mistakenly choose encryption due to misunderstanding or implementing features like “email my password” (which is inherently insecure). Remediation:- Use Hashing ONLY: Replace any encryption logic for passwords with a standard password hashing library (bcrypt, Argon2, PBKDF2, scrypt).
- Migrate Existing Passwords: If passwords are currently stored recoverably, implement a migration strategy:
- Add a new database column for the secure hash.
- When a user next logs in successfully (using the old, recoverable password), hash the provided password using the new secure method and store it in the new column.
- Clear the old recoverable password.
- Eventually, remove the column containing the recoverable passwords.
- Eliminate “Recover Password” Features: Replace “email my password” features with secure password reset links that use temporary, single-use, unpredictable tokens.
- Python
- Java
- .NET(C#)
- PHP
- Node.js
- Ruby
Framework Context
Usingcryptography’s Fernet or AES functions to encrypt passwords instead of Django’s password hashers.Vulnerable Scenario 1: Encrypting Password with Fernet
Vulnerable Scenario 2: Storing Plaintext (Covered by CWE-312, but relevant)
Mitigation and Best Practices
Use Django’s built-inUser model or AbstractUser and rely on user.set_password() and user.check_password(), which use configured secure hashers (bcrypt, Argon2, PBKDF2).Secure Code Example
Testing Strategy
Inspect the database schema and data. Look for columns explicitly storing passwords (password, pwd, secret, etc.). Determine if the stored data is a hash (long, random-looking string, often with prefixes like bcrypt$, argon2$) or encrypted/plaintext (might be Base64, hex, or directly readable). Review code responsible for setting and checking passwords; ensure it uses hashing functions (set_password, check_password, hashers.make_password, hashers.check_password), not encryption/decryption.
