Verwendung von Standardanmeldedaten

Beschreibung

Verwendung von Standardanmeldedaten tritt auf, wenn ein Produkt Standardanmeldedaten (Passworter oder kryptographische Schlüssel) für potenziell kritische Funktionalitat implementiert, wodurch Risiken zur Authentifizierungsumgehung entstehen. Produkte verwenden häufig Standardschlüssel und -passworter, um Herstellung und Systemadministration zu vereinfachen. Wenn Administratoren diese Standards jedoch nicht ändern, können Angreifer die Authentifizierung über mehrere Organisationen hinweg schnell umgehen. Listen mit Standardanmeldedaten sind weitverbreitet verfügbar, was die Ausnutzung trivial macht.

Risiko

Standardanmeldedaten haben schwerwiegende Auswirkungen. Massenausnutzung über alle nicht gepatchten Installationen. Botnet-Rekrutierung von IoT-Geräten. Vollständige Authentifizierungsumgehung. Administrativer Zugang zu kritischen Systemen. Laterale Bewegung in Netzwerken. Datenlecks. Supply-Chain-Angriffe. Verstöße gegen regulatorische Compliance. Hohe Wahrscheinlichkeit, da Standardanmeldedaten-Listen öffentlich verfügbar sind.

Lösung

Verwendung von Standard-, fest kodierten oder anderen Werten, die nicht pro Installation variieren, in der Anforderungsphase verbieten (hohe Wirksamkeit). Administrator zur Änderung der Anmeldedaten bei der Installation zwingen in der Architektur- und Designphase (hohe Wirksamkeit). Produktadministratoren können Standards während der Einrichtung oder des Betriebs ändern (mittlere Wirksamkeit). Einzigartige Anmeldedaten pro Gerat während der Herstellung generieren.

Häufige Auswirkungen

AuswirkungDetails
AuthentifizierungBereich: Authentifizierung

Angreifer können mit unveränderten Standardanmeldedaten Berechtigungen erlangen oder Identitaten annehmen.
ZugangskontrolleBereich: Zugangskontrolle

Vollständige Umgehung von Authentifizierungskontrollen durch bekannte Standardanmeldedaten.

Beispielcode

Verwundbarer Code

# VERWUNDBAR: Standardanmeldedaten in der Anwendung

# VERWUNDBAR: Im Code definierte Standardanmeldedaten
DEFAULT_ADMIN_USER = "admin"
DEFAULT_ADMIN_PASSWORD = "admin"
DEFAULT_API_KEY = "demo_key_12345"

class VulnerableApplication:
    def __init__(self):
        # VERWUNDBAR: Verwendung von Standards ohne erzwungene Änderung
        self.admin_username = DEFAULT_ADMIN_USER
        self.admin_password = DEFAULT_ADMIN_PASSWORD
        self.api_key = DEFAULT_API_KEY
        self.is_default_password = True  # Flag existiert, wird aber nicht durchgesetzt

    def authenticate(self, username, password):
        # VERWUNDBAR: Erlaubt Anmeldung mit Standardanmeldedaten
        if username == self.admin_username and password == self.admin_password:
            return True
        return False

    def verify_api_key(self, key):
        # VERWUNDBAR: Akzeptiert Standard-API-Schlüssel
        return key == self.api_key

# VERWUNDBAR: Datenbank mit Standardanmeldedaten
DATABASE_CONFIG = {
    "host": "localhost",
    "port": 5432,
    "database": "production_db",
    "user": "postgres",        # VERWUNDBAR: Standard-PostgreSQL-Benutzer
    "password": "postgres"     # VERWUNDBAR: Standardpasswort
}

# VERWUNDBAR: IoT-Gerat-Konfiguration
IOT_DEVICE_CONFIG = {
    "ssh_user": "root",
    "ssh_password": "root",           # VERWUNDBAR: Häufiger Standard
    "web_admin": "admin",
    "web_password": "admin",          # VERWUNDBAR: Standard-Webpasswort
    "telnet_enabled": True,           # VERWUNDBAR: Telnet mit Standards
    "firmware_update_key": "update123" # VERWUNDBAR: Standard-Aktualisierungsschlüssel
}

# VERWUNDBAR: Router-Standardkonfiguration
ROUTER_CONFIG = {
    "admin_user": "admin",
    "admin_pass": "password",         # VERWUNDBAR: Häufiger Router-Standard
    "wifi_password": "12345678",      # VERWUNDBAR: Schwaches Standard-WLAN
    "wps_pin": "12345670"             # VERWUNDBAR: Standard-WPS-PIN
}
// VERWUNDBAR: Java-Anwendung mit Standardanmeldedaten

public class VulnerableSystem {

    // VERWUNDBAR: Fest kodierte Standardanmeldedaten
    private static final String DEFAULT_ADMIN = "administrator";
    private static final String DEFAULT_PASSWORD = "changeme";
    private static final String DEFAULT_SECRET_KEY = "default_secret_key_123";

    private String adminPassword = DEFAULT_PASSWORD;
    private boolean passwordChanged = false;

    // VERWUNDBAR: Keine erzwungene Passwortänderung
    public boolean authenticate(String username, String password) {
        if (username.equals(DEFAULT_ADMIN) && password.equals(adminPassword)) {
            // VERWUNDBAR: Warnt, erlaubt aber Zugang mit Standard
            if (!passwordChanged) {
                System.out.println("Warnung: Standardpasswort wird verwendet");
            }
            return true;
        }
        return false;
    }

    // VERWUNDBAR: Optionale Passwortänderung
    public void changePassword(String newPassword) {
        // VERWUNDBAR: Benutzer kann diesen Schritt uberspringen
        this.adminPassword = newPassword;
        this.passwordChanged = true;
    }

    // VERWUNDBAR: Standard-JWT-Geheimnis
    public String generateToken(String userId) {
        // VERWUNDBAR: Verwendet Standard-Geheimnis wenn nicht konfiguriert
        String secret = System.getenv("JWT_SECRET");
        if (secret == null) {
            secret = DEFAULT_SECRET_KEY;  // VERWUNDBAR: Fallt auf Standard zurück
        }
        return createJWT(userId, secret);
    }
}

// VERWUNDBAR: Datenbankverbindung mit Standards
public class VulnerableDatabase {
    public Connection getConnection() throws SQLException {
        String host = getProperty("db.host", "localhost");
        String user = getProperty("db.user", "sa");        // VERWUNDBAR: Standard-SA
        String pass = getProperty("db.pass", "");          // VERWUNDBAR: Leeres Passwort

        return DriverManager.getConnection(
            "jdbc:sqlserver://" + host,
            user,
            pass
        );
    }
}
// VERWUNDBAR: Node.js mit Standardanmeldedaten

// VERWUNDBAR: Standardkonfiguration
const defaultConfig = {
    admin: {
        username: 'admin',
        password: 'admin123'  // VERWUNDBAR: Standard-Adminpasswort
    },
    database: {
        user: 'root',
        password: 'root'      // VERWUNDBAR: Standard-DB-Passwort
    },
    jwt: {
        secret: 'your-secret-key'  // VERWUNDBAR: Platzhalter-Geheimnis
    },
    encryption: {
        key: '0123456789abcdef'    // VERWUNDBAR: Standard-Verschlüsselungsschlüssel
    }
};

// VERWUNDBAR: Verwendet Standards ohne Validierung
function getConfig() {
    return {
        admin: {
            username: process.env.ADMIN_USER || defaultConfig.admin.username,
            password: process.env.ADMIN_PASS || defaultConfig.admin.password
        },
        // Fallt auf unsichere Standards zurück
    };
}

// VERWUNDBAR: Ersteinrichtung erzwingt keine Passwortänderung
async function setupApplication() {
    const config = getConfig();

    // VERWUNDBAR: Protokolliert nur Warnung, blockiert nicht
    if (config.admin.password === defaultConfig.admin.password) {
        console.warn('Warnung: Standard-Adminpasswort wird verwendet');
        // Sollte hier Passwortänderung erzwingen!
    }

    await startServer(config);
}

// VERWUNDBAR: API-Schlüssel-Generierung mit Standard-Fallback
function getApiKey() {
    return process.env.API_KEY || 'default_api_key_for_development';
}

Sichere Lösung

# SICHER: Ordnungsgemäße Behandlung von Anmeldedaten

import secrets
import os
import sys

class SecureApplication:
    def __init__(self):
        # SICHER: Keine Standardanmeldedaten
        self.admin_username = None
        self.admin_password = None
        self.api_key = None
        self.is_initialized = False

    def initialize(self):
        """SICHER: Anmeldedaten-Einrichtung vor Verwendung erzwingen."""
        if not self._credentials_configured():
            raise SecurityError(
                "Anwendung kann nicht ohne konfigurierte Anmeldedaten starten. "
                "Einrichtungsassistenten ausführen, um Admin-Anmeldedaten zu konfigurieren."
            )
        self.is_initialized = True

    def _credentials_configured(self):
        """SICHER: Prüfen ob Nicht-Standard-Anmeldedaten gesetzt sind."""
        # Umgebung oder sicheren Speicher prüfen
        username = os.environ.get('ADMIN_USERNAME')
        password = os.environ.get('ADMIN_PASSWORD')
        api_key = os.environ.get('API_KEY')

        if not all([username, password, api_key]):
            return False

        # SICHER: Bekannte Standardwerte ablehnen
        default_values = ['admin', 'password', 'changeme', 'default', '123456']
        if password.lower() in default_values:
            return False

        self.admin_username = username
        self.admin_password = password
        self.api_key = api_key
        return True

    @staticmethod
    def setup_wizard():
        """SICHER: Interaktive Einrichtung für Erstkonfiguration."""
        print("=== Ersteinrichtung ===")

        username = input("Admin-Benutzername eingeben: ").strip()

        # SICHER: Stärken Standard generieren, Bestätigung erfordern
        suggested_password = secrets.token_urlsafe(16)
        print(f"Vorgeschlagenes Passwort: {suggested_password}")

        password = input("Admin-Passwort eingeben (oder Enter für vorgeschlagenes): ").strip()
        if not password:
            password = suggested_password

        # SICHER: Passwortstärke validieren
        if not SecureApplication._is_strong_password(password):
            print("Fehler: Passwort zu schwach. Muss 12+ Zeichen mit gemischter Groß-/Kleinschreibung, Ziffern, Symbolen sein.")
            sys.exit(1)

        # SICHER: Einzigartigen API-Schlüssel generieren
        api_key = secrets.token_urlsafe(32)

        print("\nFugen Sie diese Ihrer Umgebung hinzu:")
        print(f"export ADMIN_USERNAME='{username}'")
        print(f"export ADMIN_PASSWORD='{password}'")
        print(f"export API_KEY='{api_key}'")

        return username, password, api_key

    @staticmethod
    def _is_strong_password(password):
        """SICHER: Passwortstärke validieren."""
        if len(password) < 12:
            return False
        has_upper = any(c.isupper() for c in password)
        has_lower = any(c.islower() for c in password)
        has_digit = any(c.isdigit() for c in password)
        return has_upper and has_lower and has_digit

# SICHER: Datenbankkonfiguration ohne Standards
def get_database_config():
    """SICHER: Explizite Datenbankanmeldedaten erfordern."""
    required_vars = ['DB_HOST', 'DB_NAME', 'DB_USER', 'DB_PASSWORD']

    config = {}
    missing = []

    for var in required_vars:
        value = os.environ.get(var)
        if not value:
            missing.append(var)
        else:
            config[var.lower().replace('db_', '')] = value

    if missing:
        raise EnvironmentError(
            f"Fehlende erforderliche Datenbankkonfiguration: {', '.join(missing)}"
        )

    # SICHER: Offensichtliche Standards ablehnen
    if config['password'] in ['', 'postgres', 'root', 'password']:
        raise SecurityError("Datenbankpasswort scheint ein Standardwert zu sein")

    return config
// SICHER: Java-Anwendung ohne Standardanmeldedaten

public class SecureSystem {

    // SICHER: Keine Standardanmeldedaten im Code
    private String adminPassword;
    private boolean initialized = false;

    public void initialize() throws SecurityException {
        // SICHER: Konfiguration von Anmeldedaten erfordern
        String password = System.getenv("ADMIN_PASSWORD");

        if (password == null || password.isEmpty()) {
            throw new SecurityException(
                "ADMIN_PASSWORD-Umgebungsvariable muss gesetzt sein"
            );
        }

        // SICHER: Standard-/schwache Werte ablehnen
        if (isDefaultOrWeakPassword(password)) {
            throw new SecurityException(
                "Passwort scheint ein Standard- oder schwacher Wert zu sein. " +
                "Bitte setzen Sie ein starkes, einzigartiges Passwort."
            );
        }

        this.adminPassword = password;
        this.initialized = true;
    }

    private boolean isDefaultOrWeakPassword(String password) {
        String[] defaults = {
            "admin", "password", "changeme", "default",
            "root", "123456", "administrator"
        };

        String lower = password.toLowerCase();
        for (String def : defaults) {
            if (lower.equals(def) || lower.contains(def)) {
                return true;
            }
        }

        // SICHER: Länge prüfen
        return password.length() < 12;
    }

    public boolean authenticate(String username, String password) {
        // SICHER: Initialisierung erfordern
        if (!initialized) {
            throw new IllegalStateException("System nicht initialisiert");
        }

        // Anmeldedaten verifizieren...
        return verifyPassword(password, adminPassword);
    }

    // SICHER: Erzwingung der Erstlauf-Einrichtung
    public static void main(String[] args) {
        SecureSystem system = new SecureSystem();

        try {
            system.initialize();
        } catch (SecurityException e) {
            System.err.println("FEHLER: " + e.getMessage());
            System.err.println("\nBitte Anmeldedaten konfigurieren:");
            System.err.println("  export ADMIN_PASSWORD='<starkes-passwort>'");
            System.exit(1);
        }

        // Mit initialisiertem System fortfahren
    }
}

// SICHER: Sichere JWT-Geheimnis-Behandlung
public class SecureJWT {
    private final String secret;

    public SecureJWT() {
        this.secret = loadSecret();
    }

    private String loadSecret() {
        String secret = System.getenv("JWT_SECRET");

        // SICHER: Kein Fallback auf Standard
        if (secret == null || secret.isEmpty()) {
            throw new SecurityException(
                "JWT_SECRET muss konfiguriert sein"
            );
        }

        // SICHER: Geheimnis-Stärke validieren
        if (secret.length() < 32) {
            throw new SecurityException(
                "JWT_SECRET muss mindestens 32 Zeichen lang sein"
            );
        }

        return secret;
    }
}
// SICHER: Node.js ohne Standardanmeldedaten

const crypto = require('crypto');

// SICHER: Keine Standardanmeldedaten
const requiredEnvVars = [
    'ADMIN_USERNAME',
    'ADMIN_PASSWORD',
    'JWT_SECRET',
    'DB_PASSWORD',
    'ENCRYPTION_KEY'
];

// SICHER: Konfiguration beim Start validieren
function validateConfiguration() {
    const missing = [];
    const weak = [];

    const defaultPatterns = [
        'admin', 'password', 'secret', 'changeme',
        'default', '123456', 'root', 'your-'
    ];

    for (const varName of requiredEnvVars) {
        const value = process.env[varName];

        if (!value) {
            missing.push(varName);
            continue;
        }

        // SICHER: Auf Standard-/schwache Werte prüfen
        const lower = value.toLowerCase();
        for (const pattern of defaultPatterns) {
            if (lower.includes(pattern)) {
                weak.push(varName);
                break;
            }
        }

        // SICHER: Mindestlänge für Geheimnisse prüfen
        if (varName.includes('SECRET') || varName.includes('KEY')) {
            if (value.length < 32) {
                weak.push(`${varName} (zu kurz)`);
            }
        }
    }

    if (missing.length > 0) {
        console.error('FEHLER: Fehlende erforderliche Konfiguration:');
        missing.forEach(v => console.error(`  - ${v}`));
        process.exit(1);
    }

    if (weak.length > 0) {
        console.error('FEHLER: Schwache/Standard-Werte erkannt:');
        weak.forEach(v => console.error(`  - ${v}`));
        console.error('\nBitte verwenden Sie starke, einzigartige Werte.');
        process.exit(1);
    }

    return true;
}

// SICHER: Sichere Standards für Einrichtung generieren
function generateSecureCredentials() {
    return {
        password: crypto.randomBytes(16).toString('base64'),
        jwtSecret: crypto.randomBytes(32).toString('hex'),
        encryptionKey: crypto.randomBytes(32).toString('hex')
    };
}

// SICHER: Ersteinrichtung
async function firstTimeSetup() {
    console.log('=== Ersteinrichtung ===\n');

    const credentials = generateSecureCredentials();

    console.log('Sichere Anmeldedaten generiert:');
    console.log('Fugen Sie diese Ihrer Umgebung hinzu:\n');
    console.log(`export ADMIN_PASSWORD='${credentials.password}'`);
    console.log(`export JWT_SECRET='${credentials.jwtSecret}'`);
    console.log(`export ENCRYPTION_KEY='${credentials.encryptionKey}'`);
    console.log('\nStarten Sie die Anwendung nach der Konfiguration neu.');

    process.exit(0);
}

// SICHER: Startvalidierung
async function startApplication() {
    // Prüfen ob Erstlauf
    const hasConfig = requiredEnvVars.every(v => process.env[v]);

    if (!hasConfig) {
        await firstTimeSetup();
    }

    validateConfiguration();

    // Mit validierter Konfiguration fortfahren
    console.log('Konfiguration validiert, Anwendung wird gestartet...');
}

startApplication();

CVE-Beispiele

  • CVE-2022-30270: RTU-SSH-Standardanmeldedaten ermöglichen unautorisierten Zugang.
  • CVE-2021-41192: Standard-Geheimschlüssel ermöglichen Authentifizierungsumgehung.
  • CVE-2021-38759: Mikrocontroller-Standardpasswort ermöglicht Gerateübernahme.
  • CVE-2016-1560: DVR-Systeme mit Standardanmeldedaten vom Mirai-Botnet ausgenutzt.

Verwandte CWEs

  • CWE-1391: Use of Weak Credentials (Eltern)
  • CWE-1393: Use of Default Password (Kind)
  • CWE-1394: Use of Default Cryptographic Key (Kind)
  • CWE-255: Credentials Management Errors (Kategorie)

Referenzen

  1. MITRE Corporation. "CWE-1392: Use of Default Credentials." https://cwe.mitre.org/data/definitions/1392.html
  2. CISA. "Default Passwords and Best Practices"
  3. OWASP IoT. "Default Credentials"