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
| Auswirkung | Details |
|---|---|
| Authentifizierung | Bereich: Authentifizierung Angreifer können mit unveränderten Standardanmeldedaten Berechtigungen erlangen oder Identitaten annehmen. |
| Zugangskontrolle | Bereich: 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
- MITRE Corporation. "CWE-1392: Use of Default Credentials." https://cwe.mitre.org/data/definitions/1392.html
- CISA. "Default Passwords and Best Practices"
- OWASP IoT. "Default Credentials"