Schwache Authentifizierung

Beschreibung

Schwache Authentifizierung tritt auf, wenn ein Produkt einen Authentifizierungsmechanismus implementiert, um den Zugang auf bestimmte Benutzer oder Identitäten zu beschränken, aber die beanspruchte Identität nicht ausreichend verifiziert. Angreifer können möglicherweise schwache Authentifizierung schneller und/oder mit weniger Aufwand als erwartet umgehen. Schwache Authentifizierung kann aus verschiedenen Problemen resultieren, darunter unzureichende Challenge-Stärke, vorhersagbare Authentifizierungstoken, Einzelfaktor-Authentifizierung, fehlerhafte Anmeldedatenvalidierung oder Schwachstellen zur Authentifizierungsumgehung.

Risiko

Schwache Authentifizierung hat schwerwiegende Auswirkungen. Unautorisierter Zugang zu geschützten Ressourcen. Identitätsimitation. Privilegien-Eskalation. Datenexposition. Kontoubernahme. Systemkompromittierung. Laterale Bewegung in Netzwerken. Compliance-Verstöße. Reputationsschäden. Hohe Wahrscheinlichkeit bei schlecht entworfenen oder implementierten Authentifizierungsmechanismen.

Lösung

Starke Multi-Faktor-Authentifizierung während der Architektur- und Designphase implementieren. Branchenstandard-Authentifizierungsprotokolle verwenden. Starke Passwortrichtlinien während der Implementierungsphase durchsetzen. Kontosperrmechanismen implementieren. Kryptographisch sichere Session-Token verwenden. Ordnungsgemäße Anmeldedatenspeicherung mit starkem Hashing einsetzen. Alle Authentifizierungseingaben gründlich validieren.

Häufige Auswirkungen

AuswirkungDetails
VertraulichkeitBereich: Vertraulichkeit

Offenlegung sensibler Informationen an unautorisierte Akteure durch Authentifizierungsumgehung.
IntegritatBereich: Integritat

Angreifer können legitime Benutzer imitieren und geschützte Daten ändern.
ZugangskontrolleBereich: Zugangskontrolle

Mögliche Ausführung unautorisierter Operationen und Privilegien-Eskalation.

Beispielcode

Verwundbarer Code

# VERWUNDBAR: Schwache Authentifizierungsimplementierungen

import hashlib
import time
import random

# VERWUNDBAR: Zeitstempel-basierte Authentifizierung
class VulnerableTimestampAuth:
    def authenticate(self, user_id, timestamp_token):
        # VERWUNDBAR: Token ist nur aktueller Zeitstempel
        current_time = int(time.time())

        # VERWUNDBAR: Großes Zeitfenster (5 Minuten)
        if abs(current_time - int(timestamp_token)) < 300:
            return True  # Authentifiziert

        # Angriff: Angreifer kann gültige Token vorhersagen/berechnen
        return False

# VERWUNDBAR: Schwache Session-Token-Generierung
def vulnerable_generate_session():
    # VERWUNDBAR: Vorhersagbarer Random-Seed
    random.seed(int(time.time()))

    # VERWUNDBAR: Einfache Zufallszahl als Token
    return str(random.randint(0, 999999))

# VERWUNDBAR: MD5-Passwort-Hashing (schnell, anfällig für Rainbow Tables)
def vulnerable_hash_password(password):
    # VERWUNDBAR: MD5 ist kryptographisch gebrochen
    return hashlib.md5(password.encode()).hexdigest()

# VERWUNDBAR: Kein Salt im Passwort-Hash
def vulnerable_verify_password(password, stored_hash):
    # VERWUNDBAR: Direkter Vergleich ohne Salt
    return vulnerable_hash_password(password) == stored_hash

# VERWUNDBAR: Basis-Authentifizierung ohne Rate-Limiting
class VulnerableLoginHandler:
    def __init__(self):
        self.users = {}  # username -> password_hash

    def login(self, username, password):
        # VERWUNDBAR: Kein Rate-Limiting - ermöglicht Brute Force
        # VERWUNDBAR: Keine Kontosperre
        if username in self.users:
            if vulnerable_verify_password(password, self.users[username]):
                return vulnerable_generate_session()

        return None  # Login fehlgeschlagen
// VERWUNDBAR: Java schwache Authentifizierungsmuster

import java.util.Base64;
import java.security.MessageDigest;

public class VulnerableAuth {

    // VERWUNDBAR: Basic Auth ohne ordnungsgemäße Validierung
    public boolean vulnerableBasicAuth(String authHeader) {
        if (authHeader == null || !authHeader.startsWith("Basic ")) {
            return false;
        }

        // VERWUNDBAR: Base64 ist Kodierung, nicht Verschlüsselung
        String credentials = new String(
            Base64.getDecoder().decode(authHeader.substring(6))
        );

        String[] parts = credentials.split(":");
        String username = parts[0];
        String password = parts[1];

        // VERWUNDBAR: Fest kodierte Anmeldedaten
        if ("admin".equals(username) && "admin123".equals(password)) {
            return true;
        }

        return false;
    }

    // VERWUNDBAR: Schwache Passwortverifizierung
    public boolean vulnerablePasswordCheck(String input, String stored) {
        // VERWUNDBAR: String-Vergleich anfällig für Timing-Angriffe
        return input.equals(stored);
    }

    // VERWUNDBAR: Vorhersagbare Session-ID
    public String vulnerableGenerateSession(String username) {
        // VERWUNDBAR: Vorhersagbares Session-Format
        long timestamp = System.currentTimeMillis();
        return username + "_" + timestamp;
    }

    // VERWUNDBAR: Abhängigkeit von clientseitiger Authentifizierung
    public boolean vulnerableTokenAuth(String token) {
        // VERWUNDBAR: Prüft nur ob Token existiert, nicht die Gültigkeit
        return token != null && token.length() > 0;
    }

    // VERWUNDBAR: Sicherheitsfragen-Authentifizierung
    public boolean vulnerableSecurityQuestion(String answer, String stored) {
        // VERWUNDBAR: Vergleich ohne Beachtung der Groß-/Kleinschreibung reduziert Entropie
        return answer.equalsIgnoreCase(stored);
        // Angriff: "Wie heißt Ihr Haustier?" - erratbar
    }
}
// VERWUNDBAR: JavaScript schwache Authentifizierung

// VERWUNDBAR: Clientseitige Authentifizierungsprüfung
function vulnerableClientAuth(password) {
    // VERWUNDBAR: Passwort wird in clientseitigem JavaScript geprüft
    const correctPassword = "secret123";  // Im Quellcode offengelegt

    if (password === correctPassword) {
        localStorage.setItem('authenticated', 'true');
        return true;
    }
    return false;
}

// VERWUNDBAR: Schwache JWT-Validierung
function vulnerableJWTValidation(token) {
    // VERWUNDBAR: Keine Signaturverifizierung
    try {
        const parts = token.split('.');
        const payload = JSON.parse(atob(parts[1]));

        // VERWUNDBAR: Prüft nur Ablauf, nicht Signatur
        if (payload.exp > Date.now() / 1000) {
            return payload;
        }
    } catch (e) {
        return null;
    }
    return null;
}

// VERWUNDBAR: Cookie-basierte Auth ohne Sicherheitsflags
function vulnerableSetAuthCookie(userId) {
    // VERWUNDBAR: Fehlende HttpOnly, Secure, SameSite Flags
    document.cookie = `auth=${userId}; path=/`;

    // Angriff: XSS kann dieses Cookie stehlen
}

// VERWUNDBAR: Vorhersagbarer Passwortzurücksetzungs-Token
function vulnerableGenerateResetToken(email) {
    // VERWUNDBAR: Token basiert auf E-Mail und Zeit
    const timestamp = Date.now();
    return btoa(email + ':' + timestamp);

    // Angriff: Angreifer kann gültige Token generieren
}

Sichere Lösung

# SICHER: Stärke Authentifizierungsimplementierungen

import secrets
import hashlib
import time
from datetime import datetime, timedelta
import bcrypt
import pyotp
from collections import defaultdict

# SICHER: Stärke Session-Token-Generierung
def safe_generate_session():
    # SICHER: Kryptographisch sicherer zufalliger Token
    return secrets.token_urlsafe(32)

# SICHER: Sicheres Passwort-Hashing mit bcrypt
def safe_hash_password(password):
    # SICHER: bcrypt mit automatischem Salt
    salt = bcrypt.gensalt(rounds=12)
    return bcrypt.hashpw(password.encode(), salt)

def safe_verify_password(password, stored_hash):
    # SICHER: Zeitkonstanter Vergleich in bcrypt integriert
    return bcrypt.checkpw(password.encode(), stored_hash)

# SICHER: Rate-Limiting und Kontosperre
class SafeLoginHandler:
    def __init__(self):
        self.users = {}  # username -> password_hash
        self.failed_attempts = defaultdict(list)
        self.locked_accounts = {}

    # SICHER: Konstanten für Rate-Limiting
    MAX_ATTEMPTS = 5
    LOCKOUT_DURATION = timedelta(minutes=15)
    ATTEMPT_WINDOW = timedelta(minutes=5)

    def _is_locked(self, username):
        """SICHER: Prüfen ob Konto gesperrt ist."""
        if username in self.locked_accounts:
            lock_time = self.locked_accounts[username]
            if datetime.now() < lock_time + self.LOCKOUT_DURATION:
                return True
            else:
                del self.locked_accounts[username]
        return False

    def _record_failed_attempt(self, username):
        """SICHER: Fehlgeschlagenen Anmeldeversuch aufzeichnen."""
        now = datetime.now()
        attempts = self.failed_attempts[username]

        # Alte Versuche außerhalb des Fensters entfernen
        attempts[:] = [t for t in attempts
                      if now - t < self.ATTEMPT_WINDOW]

        attempts.append(now)

        # SICHER: Konto nach maximalen Versuchen sperren
        if len(attempts) >= self.MAX_ATTEMPTS:
            self.locked_accounts[username] = now

    def login(self, username, password):
        # SICHER: Auf Kontosperre prüfen
        if self._is_locked(username):
            raise AuthenticationError("Konto gesperrt. Versuchen Sie es später erneut.")

        if username not in self.users:
            # SICHER: Konstante Zeit zur Verhinderung von Benutzerenumeration
            bcrypt.checkpw(b"dummy", bcrypt.gensalt())
            self._record_failed_attempt(username)
            return None

        if safe_verify_password(password, self.users[username]):
            # SICHER: Fehlversuche bei Erfolg loschen
            self.failed_attempts[username] = []
            return safe_generate_session()
        else:
            self._record_failed_attempt(username)
            return None

# SICHER: Multi-Faktor-Authentifizierung
class MFAHandler:
    def __init__(self):
        self.user_secrets = {}  # username -> TOTP secret

    def setup_mfa(self, username):
        """SICHER: TOTP-Geheimnis für Benutzer generieren."""
        secret = pyotp.random_base32()
        self.user_secrets[username] = secret
        return secret

    def verify_mfa(self, username, code):
        """SICHER: TOTP-Code verifizieren."""
        if username not in self.user_secrets:
            return False

        totp = pyotp.TOTP(self.user_secrets[username])
        # SICHER: Mit kleinem Zeitfenster verifizieren
        return totp.verify(code, valid_window=1)

    def full_login(self, username, password, mfa_code):
        """SICHER: Zwei-Faktor-Authentifizierung."""
        # Erster Faktor: Passwort
        if not safe_verify_password(password, self.get_password_hash(username)):
            return None

        # SICHER: Zweiter Faktor: TOTP
        if not self.verify_mfa(username, mfa_code):
            return None

        return safe_generate_session()
// SICHER: Java sichere Authentifizierung

import java.security.SecureRandom;
import java.security.MessageDigest;
import java.util.Base64;
import java.util.Arrays;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import io.jsonwebtoken.*;

public class SecureAuth {

    private static final SecureRandom SECURE_RANDOM = new SecureRandom();
    private static final int ITERATIONS = 100000;
    private static final int KEY_LENGTH = 256;

    // SICHER: Sicheres Passwort-Hashing mit PBKDF2
    public String safeHashPassword(String password) throws Exception {
        byte[] salt = new byte[16];
        SECURE_RANDOM.nextBytes(salt);

        PBEKeySpec spec = new PBEKeySpec(
            password.toCharArray(), salt, ITERATIONS, KEY_LENGTH
        );
        SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
        byte[] hash = factory.generateSecret(spec).getEncoded();

        // SICHER: Salt mit Hash speichern
        return Base64.getEncoder().encodeToString(salt) + ":" +
               Base64.getEncoder().encodeToString(hash);
    }

    // SICHER: Zeitkonstante Passwortverifizierung
    public boolean safeVerifyPassword(String password, String stored) throws Exception {
        String[] parts = stored.split(":");
        byte[] salt = Base64.getDecoder().decode(parts[0]);
        byte[] expectedHash = Base64.getDecoder().decode(parts[1]);

        PBEKeySpec spec = new PBEKeySpec(
            password.toCharArray(), salt, ITERATIONS, KEY_LENGTH
        );
        SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
        byte[] actualHash = factory.generateSecret(spec).getEncoded();

        // SICHER: Zeitkonstanter Vergleich
        return MessageDigest.isEqual(expectedHash, actualHash);
    }

    // SICHER: Kryptographisch sichere Session-ID
    public String safeGenerateSession() {
        byte[] bytes = new byte[32];
        SECURE_RANDOM.nextBytes(bytes);
        return Base64.getUrlEncoder().withoutPadding().encodeToString(bytes);
    }

    // SICHER: Ordnungsgemäße JWT-Validierung
    public Claims safeValidateJWT(String token, String secretKey) {
        try {
            // SICHER: Vollständige Signaturverifizierung
            return Jwts.parserBuilder()
                .setSigningKey(secretKey.getBytes())
                .build()
                .parseClaimsJws(token)
                .getBody();
        } catch (JwtException e) {
            return null;  // Ungültiger Token
        }
    }

    // SICHER: Sichere Token-Generierung für Passwortzurücksetzung
    public String safeGenerateResetToken() {
        byte[] bytes = new byte[32];
        SECURE_RANDOM.nextBytes(bytes);
        return Base64.getUrlEncoder().withoutPadding().encodeToString(bytes);
    }
}
// SICHER: JavaScript sichere Authentifizierung

const crypto = require('crypto');
const jwt = require('jsonwebtoken');

// SICHER: Nur serverseitige Authentifizierung
// Anmeldedaten niemals in clientseitigem Code prüfen

// SICHER: Sichere Session-Token-Generierung
function safeGenerateSession() {
    // SICHER: Kryptographisch sichere zufallige Bytes
    return crypto.randomBytes(32).toString('base64url');
}

// SICHER: Ordnungsgemäße JWT-Validierung
function safeJWTValidation(token, secretKey) {
    try {
        // SICHER: Vollständige Verifizierung einschließlich Signatur
        const payload = jwt.verify(token, secretKey, {
            algorithms: ['HS256'],  // SICHER: Algorithmus angeben
            complete: true
        });

        return payload;
    } catch (error) {
        // SICHER: Fehlgeschlagene Validierungsversuche protokollieren
        console.error('JWT-Validierung fehlgeschlagen:', error.message);
        return null;
    }
}

// SICHER: Sichere Cookie-Einstellungen
function safeSetAuthCookie(res, token) {
    res.cookie('auth', token, {
        httpOnly: true,     // SICHER: Nicht für JavaScript zuganglich
        secure: true,       // SICHER: Nur über HTTPS gesendet
        sameSite: 'strict', // SICHER: CSRF verhindern
        maxAge: 3600000,    // 1 Stunde
        path: '/'
    });
}

// SICHER: Kryptographisch sicherer Zurücksetzungs-Token
function safeGenerateResetToken() {
    // SICHER: Nicht vorhersagbarer Token
    return crypto.randomBytes(32).toString('hex');
}

// SICHER: Rate-Limiting-Middleware
const rateLimit = require('express-rate-limit');

const loginLimiter = rateLimit({
    windowMs: 15 * 60 * 1000, // 15 Minuten
    max: 5, // SICHER: Jede IP auf 5 Anfragen pro Fenster begrenzen
    message: 'Zu viele Anmeldeversuche, bitte versuchen Sie es später erneut',
    standardHeaders: true,
    legacyHeaders: false,
});

// SICHER: Zeitkonstanter Vergleich für Token
function safeCompareTokens(a, b) {
    if (typeof a !== 'string' || typeof b !== 'string') {
        return false;
    }
    // SICHER: Timing-sicherer Vergleich
    return crypto.timingSafeEqual(Buffer.from(a), Buffer.from(b));
}

CVE-Beispiele

  • CVE-2024-48445: Schwache zeitstempel-basierte Authentifizierung ermöglicht Authentifizierungsumgehung.
  • CVE-2020-8994: UART-Schnittstelle verwendet leere Root-Passworter.
  • CVE-2022-29965: Deterministische Utility-Passwortgenerierung ermöglicht Anmeldedaten-Vorhersage.

Verwandte CWEs

  • CWE-287: Improper Authentication (Eltern)
  • CWE-289: Authentication Bypass by Alternate Name (Kind)
  • CWE-290: Authentication Bypass by Spoofing (Kind)
  • CWE-308: Use of Single-factor Authentication (Kind)
  • CWE-309: Use of Password System for Primary Authentication (Kind)

Referenzen

  1. MITRE Corporation. "CWE-1390: Weak Authentication." https://cwe.mitre.org/data/definitions/1390.html
  2. OWASP. "Authentication Cheat Sheet"
  3. NIST SP 800-63B. "Digital Identity Guidelines: Authentication and Lifecycle Management"