Insufficiently Protected Credentials

Description

Insufficiently Protected Credentials occurs when a product transmits or stores authentication credentials using an insecure method that is susceptible to unauthorized interception and/or retrieval. This includes storing passwords in plaintext, using weak encryption, transmitting credentials over unencrypted channels, storing credentials in easily accessible locations, using reversible encoding instead of proper hashing, or failing to properly protect credential stores. When credentials are not adequately protected, attackers who gain any level of access can often retrieve them and escalate their privileges or move laterally through the network.

Risk

Credential protection failures have catastrophic consequences. CVE-2025-58130 in Apache Fineract exposes credentials in financial institutions, potentially leading to unauthorized access to sensitive financial data, fraud, and GDPR violations. FortiOS (FG-IR-24-111) allows privileged attackers to retrieve LDAP credentials by redirecting to malicious servers. CVE-2025-37728 in Elastic Kibana exposes cached Crowdstrike credentials across Kibana spaces, undermining security telemetry and incident response capabilities. These vulnerabilities enable attackers to impersonate legitimate users, access sensitive systems, and maintain persistent access through stolen credentials.

Solution

Never store credentials in plaintext. Use strong, adaptive hashing algorithms (Argon2, bcrypt, scrypt) for passwords. Encrypt credential stores with strong encryption (AES-256) and protect encryption keys separately. Use secure credential vaults (HashiCorp Vault, AWS Secrets Manager). Transmit credentials only over encrypted channels (TLS). Implement proper access controls on credential stores. Rotate credentials regularly. Use hardware security modules (HSMs) for critical key material. Implement multi-factor authentication to reduce credential theft impact.

Common Consequences

ImpactDetails
AuthenticationScope: Credential Theft

Attackers retrieve credentials and impersonate legitimate users.
Access ControlScope: Unauthorized Access

Stolen credentials provide access to systems, data, and network resources.
ConfidentialityScope: Lateral Movement

Credentials often work across multiple systems, enabling attackers to spread through the network.

Example Code + Solution Code

Vulnerable Code

# VULNERABLE: Storing password in plaintext
import sqlite3

def create_user(username, password):
    conn = sqlite3.connect('users.db')
    cursor = conn.cursor()
    # Password stored as plaintext - anyone with DB access can read it!
    cursor.execute("INSERT INTO users (username, password) VALUES (?, ?)",
                   (username, password))
    conn.commit()

# VULNERABLE: Reversible encoding instead of hashing
import base64

def store_password(password):
    # Base64 is encoding, NOT encryption - trivially reversible!
    encoded = base64.b64encode(password.encode())
    return encoded

# VULNERABLE: Credentials in config file
config = {
    'ldap_server': 'ldap://corp.example.com',
    'ldap_user': 'cn=admin,dc=example,dc=com',
    'ldap_password': 'AdminPassword123!'  # Plaintext credential!
}
// VULNERABLE: Weak hashing algorithm
import java.security.MessageDigest;

public class AuthService {
    public String hashPassword(String password) throws Exception {
        // MD5 is broken - rainbow tables exist for common passwords
        MessageDigest md = MessageDigest.getInstance("MD5");
        byte[] hash = md.digest(password.getBytes());
        return bytesToHex(hash);
    }

    // VULNERABLE: Credential in source code
    private static final String API_KEY = "sk-live-12345abcdef";
    private static final String DB_PASSWORD = "ProductionDBPass!";
}
// VULNERABLE: Credentials stored in localStorage
function saveCredentials(username, password) {
    // localStorage is accessible via XSS!
    localStorage.setItem('username', username);
    localStorage.setItem('password', password);  // Plaintext password!
}

// VULNERABLE: Credentials in environment without protection
const config = {
    dbHost: process.env.DB_HOST,
    dbUser: process.env.DB_USER,
    dbPassword: process.env.DB_PASSWORD  // May be visible in process listing
};

// VULNERABLE: Hardcoded credentials
const apiKey = 'sk_live_51H7...';  // Committed to version control!

Fixed Code

# SAFE: Proper password hashing with Argon2
from argon2 import PasswordHasher
from argon2.exceptions import VerifyMismatchError
import os
from cryptography.fernet import Fernet

ph = PasswordHasher(
    time_cost=3,      # Number of iterations
    memory_cost=65536, # Memory usage in KB
    parallelism=4      # Parallel threads
)

def create_user_safe(username, password):
    conn = sqlite3.connect('users.db')
    cursor = conn.cursor()

    # Hash password with Argon2 - includes salt automatically
    password_hash = ph.hash(password)

    cursor.execute("INSERT INTO users (username, password_hash) VALUES (?, ?)",
                   (username, password_hash))
    conn.commit()

def verify_password(stored_hash, provided_password):
    try:
        ph.verify(stored_hash, provided_password)
        # Check if hash needs rehashing (parameters changed)
        if ph.check_needs_rehash(stored_hash):
            return True, ph.hash(provided_password)
        return True, None
    except VerifyMismatchError:
        return False, None

# SAFE: Encrypted credential storage
class SecureCredentialStore:
    def __init__(self, key_file):
        # Key should be stored in HSM or secure vault in production
        with open(key_file, 'rb') as f:
            key = f.read()
        self.fernet = Fernet(key)

    def store_credential(self, name, credential):
        encrypted = self.fernet.encrypt(credential.encode())
        # Store encrypted credential
        self._save_to_secure_store(name, encrypted)

    def get_credential(self, name):
        encrypted = self._load_from_secure_store(name)
        return self.fernet.decrypt(encrypted).decode()
// SAFE: Strong password hashing with BCrypt
import org.mindrot.jbcrypt.BCrypt;

public class SecureAuthService {
    private static final int BCRYPT_ROUNDS = 12;

    public String hashPassword(String password) {
        // BCrypt includes salt automatically
        return BCrypt.hashpw(password, BCrypt.gensalt(BCRYPT_ROUNDS));
    }

    public boolean verifyPassword(String password, String storedHash) {
        return BCrypt.checkpw(password, storedHash);
    }
}

// SAFE: Credentials from secure vault
import com.bettercloud.vault.Vault;

public class SecureConfig {
    private final Vault vault;

    public SecureConfig() {
        VaultConfig config = new VaultConfig()
            .address(System.getenv("VAULT_ADDR"))
            .token(System.getenv("VAULT_TOKEN"))
            .build();
        this.vault = new Vault(config);
    }

    public String getApiKey() throws VaultException {
        return vault.logical()
            .read("secret/data/api-keys")
            .getData()
            .get("api_key");
    }

    public String getDbPassword() throws VaultException {
        // Vault can provide dynamic, short-lived credentials
        return vault.logical()
            .read("database/creds/my-role")
            .getData()
            .get("password");
    }
}
// SAFE: Never store credentials in localStorage
// Use HTTP-only cookies for session tokens instead
function setAuthToken(token) {
    // Server sets HTTP-only cookie, not accessible via JavaScript
    // fetch('/api/auth/token', { credentials: 'include' });
}

// SAFE: Use environment variables with proper protection
const { SecretManagerServiceClient } = require('@google-cloud/secret-manager');

async function getCredentials() {
    const client = new SecretManagerServiceClient();

    // Access secret from cloud provider's secret manager
    const [version] = await client.accessSecretVersion({
        name: 'projects/my-project/secrets/db-password/versions/latest',
    });

    return version.payload.data.toString();
}

// SAFE: Encrypted credential file with proper key management
const crypto = require('crypto');
const fs = require('fs');

class SecureCredentialManager {
    constructor(keyPath) {
        // Key loaded from secure location (HSM, KMS, etc.)
        this.key = fs.readFileSync(keyPath);
    }

    encrypt(credential) {
        const iv = crypto.randomBytes(16);
        const cipher = crypto.createCipheriv('aes-256-gcm', this.key, iv);
        let encrypted = cipher.update(credential, 'utf8', 'hex');
        encrypted += cipher.final('hex');
        const authTag = cipher.getAuthTag();
        return { iv: iv.toString('hex'), encrypted, authTag: authTag.toString('hex') };
    }

    decrypt(encryptedData) {
        const decipher = crypto.createDecipheriv(
            'aes-256-gcm',
            this.key,
            Buffer.from(encryptedData.iv, 'hex')
        );
        decipher.setAuthTag(Buffer.from(encryptedData.authTag, 'hex'));
        let decrypted = decipher.update(encryptedData.encrypted, 'hex', 'utf8');
        decrypted += decipher.final('utf8');
        return decrypted;
    }
}

Exploited in the Wild

Apache Fineract Credential Exposure (Apache, 2025)

CVE-2025-58130 in Apache Fineract up to 1.11.0 exposes insufficiently protected credentials, enabling unauthorized access to financial data, customer information, and transactional systems in microfinance and fintech environments, with potential for fraud and GDPR violations.

FortiOS LDAP Credential Theft (Fortinet, 2024-2025)

FG-IR-24-111 in FortiOS allows privileged attackers to retrieve LDAP credentials by redirecting LDAP server configuration to attacker-controlled servers, demonstrating credential interception through configuration manipulation.

Elastic Kibana Crowdstrike Credential Leak (Elastic, 2025)

CVE-2025-37728 in Kibana allows low-privileged attackers to access cached Crowdstrike credentials from other Kibana spaces, potentially compromising security monitoring and incident response capabilities.


Tools to test/exploit

  • TruffleHog — find leaked credentials in code.

  • Hashcat — password hash cracking.

  • CyberChef — decode/decrypt weak credential encoding.


CVE Examples


References

  1. MITRE. "CWE-522: Insufficiently Protected Credentials." https://cwe.mitre.org/data/definitions/522.html

  2. OWASP. "Password Storage Cheat Sheet." https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html