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
| Impact | Details |
|---|---|
| Access Control | Scope: Access Control An adversary could bypass intended authentication restrictions using weak, default, or compromised credentials. |
| Confidentiality | Scope: 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.
Related CWEs
- 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
- MITRE Corporation. "CWE-1391: Use of Weak Credentials." https://cwe.mitre.org/data/definitions/1391.html
- NIST SP 800-63B. "Digital Identity Guidelines"
- OWASP. "Credential Stuffing Prevention Cheat Sheet"