Use of Weak Credentials

Description

Use of Weak Credentials occurs when a product uses weak credentials (such as a default key or hard-coded password) that can be calculated, derived, reused, or guessed by an attacker. Authentication protocols aim to force attackers into brute force attacks when lacking valid credentials. However, easily predictable or fixed credentials undermine this protection. Weak credentials arise from multiple sources: hard-coded credentials that are static and unchangeable by administrators, default credentials that are the same across installations, predictable credentials that are unique per deployment yet guessable with reasonable effort, and previously compromised credentials that have been leaked from data breaches.

Risk

Weak credentials have severe implications. Authentication bypass across multiple installations. Mass compromise of devices sharing credentials. Credential stuffing attacks. Password spraying success. Default credential exploitation. Hard-coded secret extraction. Predictable key generation. Brute force feasibility. High likelihood when credential policies are weak.

Solution

Validate passwords against compromised password databases during password changes and setup (moderate effectiveness). Prohibit use of default, hard-coded, or predictable credentials during requirements phase. Force unique credential generation per installation during architecture and design phase. Implement strong password policies during implementation phase. Use cryptographically secure random generation for any programmatic credentials.

Common Consequences

ImpactDetails
Access ControlScope: Access Control

An adversary could bypass intended authentication restrictions using weak, default, or compromised credentials.
ConfidentialityScope: Confidentiality

Unauthorized access to sensitive data through credential compromise.

Example Code

Vulnerable Code

# Vulnerable: Various weak credential patterns

import hashlib
import random
import string

# VULNERABLE: Hard-coded credentials
class VulnerableHardcodedAuth:
    # VULNERABLE: Credentials in source code
    ADMIN_USERNAME = "admin"
    ADMIN_PASSWORD = "admin123"
    API_KEY = "sk_live_abcd1234efgh5678"

    def authenticate(self, username, password):
        # VULNERABLE: Hard-coded comparison
        return (username == self.ADMIN_USERNAME and
                password == self.ADMIN_PASSWORD)

    def verify_api_key(self, key):
        # VULNERABLE: Hard-coded API key
        return key == self.API_KEY

# VULNERABLE: Predictable credential generation
class VulnerablePredictableCredentials:
    def generate_password(self, username):
        # VULNERABLE: Predictable pattern
        # Password is username reversed + "123"
        return username[::-1] + "123"

    def generate_api_key(self, user_id):
        # VULNERABLE: Sequential/predictable keys
        return f"api_key_{user_id}_{hash(user_id) % 10000}"

    def generate_temp_password(self):
        # VULNERABLE: Weak random source
        random.seed(int(time.time()))  # Predictable seed
        chars = string.ascii_letters + string.digits
        return ''.join(random.choice(chars) for _ in range(8))

# VULNERABLE: Weak password policy
class VulnerablePasswordPolicy:
    def validate_password(self, password):
        # VULNERABLE: Minimal requirements
        if len(password) >= 4:  # Too short
            return True
        return False

    def is_password_strong(self, password):
        # VULNERABLE: No complexity requirements
        # VULNERABLE: No check against common passwords
        # VULNERABLE: No check against breached passwords
        return len(password) >= 6

# VULNERABLE: Default credentials in configuration
DEFAULT_CONFIG = {
    "database": {
        "host": "localhost",
        "username": "root",          # VULNERABLE: Default DB user
        "password": "password123",   # VULNERABLE: Default password
    },
    "admin": {
        "username": "administrator", # VULNERABLE: Obvious username
        "password": "changeme",      # VULNERABLE: Never changed
    },
    "encryption_key": "0123456789abcdef"  # VULNERABLE: Default key
}
// Vulnerable: Java weak credential patterns

public class VulnerableCredentials {

    // VULNERABLE: Hard-coded credentials
    private static final String DB_USER = "sa";
    private static final String DB_PASS = "";  // Empty password!

    // VULNERABLE: Hard-coded encryption key
    private static final byte[] ENCRYPTION_KEY =
        "MySuperSecretKey".getBytes();  // 16 bytes for AES

    // VULNERABLE: Predictable token generation
    public String generateToken(String userId) {
        // VULNERABLE: Based on current time - predictable
        long timestamp = System.currentTimeMillis();
        return userId + "_" + timestamp;
    }

    // VULNERABLE: Weak password generation
    public String generatePassword() {
        // VULNERABLE: Only lowercase letters
        StringBuilder sb = new StringBuilder();
        Random rand = new Random();  // VULNERABLE: Not SecureRandom

        for (int i = 0; i < 6; i++) {  // VULNERABLE: Only 6 chars
            sb.append((char)('a' + rand.nextInt(26)));
        }
        return sb.toString();
    }

    // VULNERABLE: Credential derivation from public info
    public String deriveCredential(String serialNumber) {
        // VULNERABLE: Derived from device serial number
        return "device_" + serialNumber.hashCode();
    }

    // VULNERABLE: Default credentials check
    public boolean isDefaultPassword(String password) {
        // These should be REJECTED, not just checked
        String[] defaults = {"admin", "password", "123456", "root"};
        for (String def : defaults) {
            if (password.equals(def)) {
                return true;  // But doesn't prevent use!
            }
        }
        return false;
    }
}
// Vulnerable: JavaScript/Node.js weak credentials

// VULNERABLE: Credentials in source code
const config = {
    jwt_secret: "secret123",  // VULNERABLE: Weak, hard-coded
    api_key: "1234567890",    // VULNERABLE: Simple numeric
    admin_password: "admin"    // VULNERABLE: Default
};

// VULNERABLE: Predictable session ID
function vulnerableGenerateSessionId(userId) {
    // VULNERABLE: Sequential and predictable
    return `session_${userId}_${Date.now()}`;
}

// VULNERABLE: Weak password validation
function vulnerableValidatePassword(password) {
    // VULNERABLE: Only checks length
    return password.length >= 4;
}

// VULNERABLE: Password from environment without validation
function getDbPassword() {
    // VULNERABLE: May be empty or default
    return process.env.DB_PASSWORD || "default_password";
}

// VULNERABLE: Credential generation from username
function generateInitialPassword(username) {
    // VULNERABLE: Predictable pattern
    return username + "123!";
}

// VULNERABLE: Using compromised password list insufficiently
const commonPasswords = ["123456", "password", "admin"];

function checkPassword(password) {
    // VULNERABLE: List too small, easily bypassed
    return !commonPasswords.includes(password);
}

Fixed Code

# Fixed: Strong credential management

import secrets
import string
import hashlib
import os
import requests
from functools import lru_cache

# FIXED: Secure credential generation
class SecureCredentialGenerator:
    # FIXED: No hard-coded credentials

    @staticmethod
    def generate_password(length=16):
        """FIXED: Cryptographically secure password generation."""
        alphabet = string.ascii_letters + string.digits + string.punctuation
        # FIXED: Using secrets module for cryptographic randomness
        return ''.join(secrets.choice(alphabet) for _ in range(length))

    @staticmethod
    def generate_api_key():
        """FIXED: Cryptographically secure API key."""
        return secrets.token_urlsafe(32)

    @staticmethod
    def generate_encryption_key(length=32):
        """FIXED: Cryptographically secure encryption key."""
        return secrets.token_bytes(length)

# FIXED: Strong password policy with breach checking
class SecurePasswordPolicy:
    MIN_LENGTH = 12
    REQUIRE_UPPERCASE = True
    REQUIRE_LOWERCASE = True
    REQUIRE_DIGITS = True
    REQUIRE_SPECIAL = True

    def __init__(self):
        self.common_passwords = self._load_common_passwords()

    def _load_common_passwords(self):
        """FIXED: Load comprehensive list of common passwords."""
        # In production, use a larger list like rockyou or similar
        return set()  # Placeholder

    def validate_password(self, password, username=None):
        """FIXED: Comprehensive password validation."""
        errors = []

        # FIXED: Length check
        if len(password) < self.MIN_LENGTH:
            errors.append(f"Password must be at least {self.MIN_LENGTH} characters")

        # FIXED: Complexity checks
        if self.REQUIRE_UPPERCASE and not any(c.isupper() for c in password):
            errors.append("Password must contain uppercase letter")

        if self.REQUIRE_LOWERCASE and not any(c.islower() for c in password):
            errors.append("Password must contain lowercase letter")

        if self.REQUIRE_DIGITS and not any(c.isdigit() for c in password):
            errors.append("Password must contain digit")

        if self.REQUIRE_SPECIAL and not any(c in string.punctuation for c in password):
            errors.append("Password must contain special character")

        # FIXED: Check against username
        if username and username.lower() in password.lower():
            errors.append("Password cannot contain username")

        # FIXED: Check against common passwords
        if password.lower() in self.common_passwords:
            errors.append("Password is too common")

        # FIXED: Check against breached passwords (Have I Been Pwned)
        if self._is_breached_password(password):
            errors.append("Password has been exposed in a data breach")

        return len(errors) == 0, errors

    def _is_breached_password(self, password):
        """FIXED: Check password against Have I Been Pwned API."""
        # Hash password with SHA-1
        sha1_hash = hashlib.sha1(password.encode()).hexdigest().upper()
        prefix = sha1_hash[:5]
        suffix = sha1_hash[5:]

        try:
            # FIXED: k-Anonymity API - only sends prefix
            response = requests.get(
                f"https://api.pwnedpasswords.com/range/{prefix}",
                timeout=5
            )
            if response.status_code == 200:
                hashes = response.text.splitlines()
                for line in hashes:
                    hash_suffix, count = line.split(':')
                    if hash_suffix == suffix:
                        return True  # Password found in breach
        except requests.RequestException:
            pass  # API unavailable, continue

        return False

# FIXED: Secure configuration management
class SecureConfig:
    def __init__(self):
        # FIXED: Load from environment or secure vault
        self.db_password = self._get_secret('DB_PASSWORD')
        self.api_key = self._get_secret('API_KEY')
        self.encryption_key = self._get_secret('ENCRYPTION_KEY')

    def _get_secret(self, name):
        """FIXED: Get secret from secure source."""
        # Check environment variable
        value = os.environ.get(name)

        if not value:
            raise ValueError(f"Required secret {name} not configured")

        # FIXED: Validate it's not a default/weak value
        weak_values = ['password', 'secret', 'changeme', 'default', '']
        if value.lower() in weak_values:
            raise ValueError(f"Secret {name} appears to be a default value")

        return value
// Fixed: Java secure credential management

import java.security.SecureRandom;
import java.util.Base64;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;

public class SecureCredentials {

    private static final SecureRandom SECURE_RANDOM = new SecureRandom();

    // FIXED: No hard-coded credentials - load from secure source
    private final String dbPassword;
    private final byte[] encryptionKey;

    public SecureCredentials() {
        // FIXED: Load from environment or vault
        this.dbPassword = loadSecret("DB_PASSWORD");
        this.encryptionKey = loadKeyFromVault("ENCRYPTION_KEY");

        // FIXED: Validate loaded credentials
        validateCredentials();
    }

    private String loadSecret(String name) {
        String value = System.getenv(name);
        if (value == null || value.isEmpty()) {
            throw new SecurityException("Required secret not configured: " + name);
        }

        // FIXED: Reject obviously weak values
        if (isWeakCredential(value)) {
            throw new SecurityException("Weak credential detected for: " + name);
        }

        return value;
    }

    private boolean isWeakCredential(String value) {
        String[] weakPatterns = {
            "password", "secret", "admin", "root",
            "changeme", "default", "12345"
        };

        String lower = value.toLowerCase();
        for (String pattern : weakPatterns) {
            if (lower.contains(pattern)) {
                return true;
            }
        }

        // FIXED: Also check length
        return value.length() < 12;
    }

    // FIXED: Cryptographically secure token generation
    public String generateSecureToken() {
        byte[] bytes = new byte[32];
        SECURE_RANDOM.nextBytes(bytes);
        return Base64.getUrlEncoder().withoutPadding().encodeToString(bytes);
    }

    // FIXED: Strong password generation
    public String generateSecurePassword(int length) {
        if (length < 16) {
            length = 16;  // FIXED: Minimum length
        }

        String chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*";
        StringBuilder sb = new StringBuilder();

        for (int i = 0; i < length; i++) {
            int index = SECURE_RANDOM.nextInt(chars.length());
            sb.append(chars.charAt(index));
        }

        // FIXED: Ensure complexity requirements
        String password = sb.toString();
        while (!meetsComplexity(password)) {
            password = generateSecurePassword(length);
        }

        return password;
    }

    private boolean meetsComplexity(String password) {
        boolean hasUpper = password.chars().anyMatch(Character::isUpperCase);
        boolean hasLower = password.chars().anyMatch(Character::isLowerCase);
        boolean hasDigit = password.chars().anyMatch(Character::isDigit);
        boolean hasSpecial = password.chars().anyMatch(c -> "!@#$%^&*".indexOf(c) >= 0);

        return hasUpper && hasLower && hasDigit && hasSpecial;
    }

    // FIXED: Generate cryptographic key properly
    public SecretKey generateEncryptionKey() throws Exception {
        KeyGenerator keyGen = KeyGenerator.getInstance("AES");
        keyGen.init(256, SECURE_RANDOM);  // FIXED: 256-bit key
        return keyGen.generateKey();
    }
}

CVE Examples

  • CVE-2021-41192: Default secret keys enabling authentication bypass.
  • CVE-2019-9013: IoT devices using default SSH credentials.
  • CVE-2020-29583: Cryptocurrency library falling back to insecure randomization.

  • CWE-1390: Weak Authentication (parent)
  • CWE-521: Weak Password Requirements (child)
  • CWE-798: Use of Hard-coded Credentials (child)
  • CWE-1392: Use of Default Credentials (child)

References

  1. MITRE Corporation. "CWE-1391: Use of Weak Credentials." https://cwe.mitre.org/data/definitions/1391.html
  2. NIST SP 800-63B. "Digital Identity Guidelines"
  3. OWASP. "Credential Stuffing Prevention Cheat Sheet"