Verwendung schwacher Anmeldedaten
Beschreibung
Verwendung schwacher Anmeldedaten tritt auf, wenn ein Produkt schwache Anmeldedaten verwendet (wie einen Standardschlüssel oder ein fest kodiertes Passwort), die von einem Angreifer berechnet, abgeleitet, wiederverwendet oder erraten werden können. Authentifizierungsprotokolle zielen darauf ab, Angreifer ohne gültige Anmeldedaten zu Brute-Force-Angriffen zu zwingen. Leicht vorhersagbare oder feste Anmeldedaten untergraben jedoch diesen Schutz. Schwache Anmeldedaten entstehen aus mehreren Quellen: fest kodierte Anmeldedaten, die statisch und von Administratoren nicht änderbar sind, Standardanmeldedaten, die über Installationen hinweg gleich sind, vorhersagbare Anmeldedaten, die pro Bereitstellung einzigartig, aber mit vertretbarem Aufwand erratbar sind, und zuvor kompromittierte Anmeldedaten, die aus Datenlecks stammen.
Risiko
Schwache Anmeldedaten haben schwerwiegende Auswirkungen. Authentifizierungsumgehung über mehrere Installationen hinweg. Massenkompromittierung von Geräten mit gemeinsamen Anmeldedaten. Credential-Stuffing-Angriffe. Erfolg bei Password-Spraying. Ausnutzung von Standardanmeldedaten. Extraktion fest kodierter Geheimnisse. Vorhersagbare Schlüsselgenerierung. Machbarkeit von Brute-Force-Angriffen. Hohe Wahrscheinlichkeit bei schwachen Anmeldedatenrichtlinien.
Lösung
Passwörter bei Passwortänderungen und Ersteinrichtung gegen Datenbanken kompromittierter Passwörter validieren (mittlere Wirksamkeit). Verwendung von Standard-, fest kodierten oder vorhersagbaren Anmeldedaten in der Anforderungsphase verbieten. Einzigartige Anmeldedatengenerierung pro Installation in der Architektur- und Designphase erzwingen. Starke Passwortrichtlinien in der Implementierungsphase umsetzen. Kryptographisch sichere Zufallsgenerierung für alle programmatischen Anmeldedaten verwenden.
Häufige Auswirkungen
| Auswirkung | Details |
|---|---|
| Zugangskontrolle | Bereich: Zugangskontrolle Ein Angreifer könnte beabsichtigte Authentifizierungsbeschrankungen mit schwachen, Standard- oder kompromittierten Anmeldedaten umgehen. |
| Vertraulichkeit | Bereich: Vertraulichkeit Unautorisierter Zugang zu sensiblen Daten durch Kompromittierung von Anmeldedaten. |
Beispielcode
Verwundbarer Code
# VERWUNDBAR: Verschiedene schwache Anmeldedatenmuster
import hashlib
import random
import string
# VERWUNDBAR: Fest kodierte Anmeldedaten
class VulnerableHardcodedAuth:
# VERWUNDBAR: Anmeldedaten im Quellcode
ADMIN_USERNAME = "admin"
ADMIN_PASSWORD = "admin123"
API_KEY = "sk_live_abcd1234efgh5678"
def authenticate(self, username, password):
# VERWUNDBAR: Fest kodierter Vergleich
return (username == self.ADMIN_USERNAME and
password == self.ADMIN_PASSWORD)
def verify_api_key(self, key):
# VERWUNDBAR: Fest kodierter API-Schlüssel
return key == self.API_KEY
# VERWUNDBAR: Vorhersagbare Anmeldedatengenerierung
class VulnerablePredictableCredentials:
def generate_password(self, username):
# VERWUNDBAR: Vorhersagbares Muster
# Passwort ist Benutzername rückwärts + "123"
return username[::-1] + "123"
def generate_api_key(self, user_id):
# VERWUNDBAR: Sequentielle/vorhersagbare Schlüssel
return f"api_key_{user_id}_{hash(user_id) % 10000}"
def generate_temp_password(self):
# VERWUNDBAR: Schwache Zufallsquelle
random.seed(int(time.time())) # Vorhersagbarer Seed
chars = string.ascii_letters + string.digits
return ''.join(random.choice(chars) for _ in range(8))
# VERWUNDBAR: Schwache Passwortrichtlinie
class VulnerablePasswordPolicy:
def validate_password(self, password):
# VERWUNDBAR: Minimale Anforderungen
if len(password) >= 4: # Zu kurz
return True
return False
def is_password_strong(self, password):
# VERWUNDBAR: Keine Komplexitätsanforderungen
# VERWUNDBAR: Keine Prüfung gegen gängige Passwörter
# VERWUNDBAR: Keine Prüfung gegen geleakte Passwörter
return len(password) >= 6
# VERWUNDBAR: Standardanmeldedaten in der Konfiguration
DEFAULT_CONFIG = {
"database": {
"host": "localhost",
"username": "root", # VERWUNDBAR: Standard-DB-Benutzer
"password": "password123", # VERWUNDBAR: Standardpasswort
},
"admin": {
"username": "administrator", # VERWUNDBAR: Offensichtlicher Benutzername
"password": "changeme", # VERWUNDBAR: Nie geändert
},
"encryption_key": "0123456789abcdef" # VERWUNDBAR: Standardschlüssel
}
// VERWUNDBAR: Java schwache Anmeldedatenmuster
public class VulnerableCredentials {
// VERWUNDBAR: Fest kodierte Anmeldedaten
private static final String DB_USER = "sa";
private static final String DB_PASS = ""; // Leeres Passwort!
// VERWUNDBAR: Fest kodierter Verschlüsselungsschlüssel
private static final byte[] ENCRYPTION_KEY =
"MySuperSecretKey".getBytes(); // 16 Bytes für AES
// VERWUNDBAR: Vorhersagbare Token-Generierung
public String generateToken(String userId) {
// VERWUNDBAR: Basiert auf aktueller Zeit - vorhersagbar
long timestamp = System.currentTimeMillis();
return userId + "_" + timestamp;
}
// VERWUNDBAR: Schwache Passwortgenerierung
public String generatePassword() {
// VERWUNDBAR: Nur Kleinbuchstaben
StringBuilder sb = new StringBuilder();
Random rand = new Random(); // VERWUNDBAR: Nicht SecureRandom
for (int i = 0; i < 6; i++) { // VERWUNDBAR: Nur 6 Zeichen
sb.append((char)('a' + rand.nextInt(26)));
}
return sb.toString();
}
// VERWUNDBAR: Anmeldedatenableitung aus offentlichen Informationen
public String deriveCredential(String serialNumber) {
// VERWUNDBAR: Abgeleitet von Gerats-Seriennummer
return "device_" + serialNumber.hashCode();
}
// VERWUNDBAR: Standardanmeldedaten-Prüfung
public boolean isDefaultPassword(String password) {
// Diese sollten ABGELEHNT werden, nicht nur geprüft
String[] defaults = {"admin", "password", "123456", "root"};
for (String def : defaults) {
if (password.equals(def)) {
return true; // Verhindert aber nicht die Verwendung!
}
}
return false;
}
}
// VERWUNDBAR: JavaScript/Node.js schwache Anmeldedaten
// VERWUNDBAR: Anmeldedaten im Quellcode
const config = {
jwt_secret: "secret123", // VERWUNDBAR: Schwach, fest kodiert
api_key: "1234567890", // VERWUNDBAR: Einfach numerisch
admin_password: "admin" // VERWUNDBAR: Standard
};
// VERWUNDBAR: Vorhersagbare Session-ID
function vulnerableGenerateSessionId(userId) {
// VERWUNDBAR: Sequentiell und vorhersagbar
return `session_${userId}_${Date.now()}`;
}
// VERWUNDBAR: Schwache Passwortvalidierung
function vulnerableValidatePassword(password) {
// VERWUNDBAR: Prüft nur Länge
return password.length >= 4;
}
// VERWUNDBAR: Passwort aus Umgebung ohne Validierung
function getDbPassword() {
// VERWUNDBAR: Konnte leer oder Standard sein
return process.env.DB_PASSWORD || "default_password";
}
// VERWUNDBAR: Anmeldedatengenerierung aus Benutzernamen
function generateInitialPassword(username) {
// VERWUNDBAR: Vorhersagbares Muster
return username + "123!";
}
// VERWUNDBAR: Unzureichende Verwendung kompromittierter Passwortliste
const commonPasswords = ["123456", "password", "admin"];
function checkPassword(password) {
// VERWUNDBAR: Liste zu klein, leicht umgehbar
return !commonPasswords.includes(password);
}
Sichere Lösung
# SICHER: Stärke Anmeldedatenverwaltung
import secrets
import string
import hashlib
import os
import requests
from functools import lru_cache
# SICHER: Sichere Anmeldedatengenerierung
class SecureCredentialGenerator:
# SICHER: Keine fest kodierten Anmeldedaten
@staticmethod
def generate_password(length=16):
"""SICHER: Kryptographisch sichere Passwortgenerierung."""
alphabet = string.ascii_letters + string.digits + string.punctuation
# SICHER: secrets-Modul für kryptographische Zufalligkeit verwenden
return ''.join(secrets.choice(alphabet) for _ in range(length))
@staticmethod
def generate_api_key():
"""SICHER: Kryptographisch sicherer API-Schlüssel."""
return secrets.token_urlsafe(32)
@staticmethod
def generate_encryption_key(length=32):
"""SICHER: Kryptographisch sicherer Verschlüsselungsschlüssel."""
return secrets.token_bytes(length)
# SICHER: Stärke Passwortrichtlinie mit Breach-Prüfung
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):
"""SICHER: Umfassende Liste gängiger Passwörter laden."""
# In Produktion eine größere Liste wie rockyou oder ähnlich verwenden
return set() # Platzhalter
def validate_password(self, password, username=None):
"""SICHER: Umfassende Passwortvalidierung."""
errors = []
# SICHER: Längenprüfung
if len(password) < self.MIN_LENGTH:
errors.append(f"Passwort muss mindestens {self.MIN_LENGTH} Zeichen lang sein")
# SICHER: Komplexitatsprüfungen
if self.REQUIRE_UPPERCASE and not any(c.isupper() for c in password):
errors.append("Passwort muss Großbuchstaben enthalten")
if self.REQUIRE_LOWERCASE and not any(c.islower() for c in password):
errors.append("Passwort muss Kleinbuchstaben enthalten")
if self.REQUIRE_DIGITS and not any(c.isdigit() for c in password):
errors.append("Passwort muss Ziffern enthalten")
if self.REQUIRE_SPECIAL and not any(c in string.punctuation for c in password):
errors.append("Passwort muss Sonderzeichen enthalten")
# SICHER: Gegen Benutzernamen prüfen
if username and username.lower() in password.lower():
errors.append("Passwort darf keinen Benutzernamen enthalten")
# SICHER: Gegen gängige Passwörter prüfen
if password.lower() in self.common_passwords:
errors.append("Passwort ist zu häufig")
# SICHER: Gegen geleakte Passwörter prüfen (Have I Been Pwned)
if self._is_breached_password(password):
errors.append("Passwort wurde in einem Datenleck offengelegt")
return len(errors) == 0, errors
def _is_breached_password(self, password):
"""SICHER: Passwort gegen Have I Been Pwned API prüfen."""
# Passwort mit SHA-1 hashen
sha1_hash = hashlib.sha1(password.encode()).hexdigest().upper()
prefix = sha1_hash[:5]
suffix = sha1_hash[5:]
try:
# SICHER: k-Anonymitats-API - sendet nur Prafix
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 # Passwort in Breach gefunden
except requests.RequestException:
pass # API nicht verfügbar, fortfahren
return False
# SICHER: Sichere Konfigurationsverwaltung
class SecureConfig:
def __init__(self):
# SICHER: Aus Umgebung oder sicherem Tresor laden
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):
"""SICHER: Geheimnis aus sicherer Quelle holen."""
# Umgebungsvariable prüfen
value = os.environ.get(name)
if not value:
raise ValueError(f"Erforderliches Geheimnis {name} nicht konfiguriert")
# SICHER: Validieren, dass es kein Standard-/schwacher Wert ist
weak_values = ['password', 'secret', 'changeme', 'default', '']
if value.lower() in weak_values:
raise ValueError(f"Geheimnis {name} scheint ein Standardwert zu sein")
return value
// SICHER: Java sichere Anmeldedatenverwaltung
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();
// SICHER: Keine fest kodierten Anmeldedaten - aus sicherer Quelle laden
private final String dbPassword;
private final byte[] encryptionKey;
public SecureCredentials() {
// SICHER: Aus Umgebung oder Tresor laden
this.dbPassword = loadSecret("DB_PASSWORD");
this.encryptionKey = loadKeyFromVault("ENCRYPTION_KEY");
// SICHER: Geladene Anmeldedaten validieren
validateCredentials();
}
private String loadSecret(String name) {
String value = System.getenv(name);
if (value == null || value.isEmpty()) {
throw new SecurityException("Erforderliches Geheimnis nicht konfiguriert: " + name);
}
// SICHER: Offensichtlich schwache Werte ablehnen
if (isWeakCredential(value)) {
throw new SecurityException("Schwache Anmeldedaten erkannt fur: " + 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;
}
}
// SICHER: Auch Länge prüfen
return value.length() < 12;
}
// SICHER: Kryptographisch sichere Token-Generierung
public String generateSecureToken() {
byte[] bytes = new byte[32];
SECURE_RANDOM.nextBytes(bytes);
return Base64.getUrlEncoder().withoutPadding().encodeToString(bytes);
}
// SICHER: Stärke Passwortgenerierung
public String generateSecurePassword(int length) {
if (length < 16) {
length = 16; // SICHER: Mindestlänge
}
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));
}
// SICHER: Komplexitätsanforderungen sicherstellen
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;
}
// SICHER: Kryptographischen Schlüssel ordnungsgemäß generieren
public SecretKey generateEncryptionKey() throws Exception {
KeyGenerator keyGen = KeyGenerator.getInstance("AES");
keyGen.init(256, SECURE_RANDOM); // SICHER: 256-Bit-Schlüssel
return keyGen.generateKey();
}
}
CVE-Beispiele
- CVE-2021-41192: Standard-Geheimschlüssel ermöglichen Authentifizierungsumgehung.
- CVE-2019-9013: IoT-Gerate verwenden Standard-SSH-Anmeldedaten.
- CVE-2020-29583: Kryptowährungsbibliothek fallt auf unsichere Randomisierung zurück.
Verwandte CWEs
- CWE-1390: Weak Authentication (Eltern)
- CWE-521: Weak Password Requirements (Kind)
- CWE-798: Use of Hard-coded Credentials (Kind)
- CWE-1392: Use of Default Credentials (Kind)
Referenzen
- 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"