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

AuswirkungDetails
ZugangskontrolleBereich: Zugangskontrolle

Ein Angreifer könnte beabsichtigte Authentifizierungsbeschrankungen mit schwachen, Standard- oder kompromittierten Anmeldedaten umgehen.
VertraulichkeitBereich: 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

  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"