Fehlender kritischer Schritt in der Authentifizierung

Beschreibung

Fehlender kritischer Schritt in der Authentifizierung ist eine Schwachstelle, die auftritt, wenn ein Produkt eine Authentifizierungstechnik implementiert, aber einen kritischen Schritt auslässt, der die gesamte Technik schwacht. Authentifizierungsalgorithmen sind mit bestimmten Sequenzen von Verifizierungsschritten konzipiert, die jeweils einen Zweck bei der Sicherstellung der Authentizität von Benutzern oder Systemen erfüllen. Wenn Implementierungen Schritte überspringen -- sei es durch Versehen, Optimierungsversuche oder unvollständiges Verständnis des Protokolls -- wird der Authentifizierungsmechanismus anfällig für Umgehungsangriffe. Häufige Beispiele umfassen das Überspringen der Signaturverifizierung, das Auslassen der Validierung gemeinsamer Geheimnisse oder das Umgehen von Protokoll-Initialisierungsschritten.

Risiko

Das Überspringen von Authentifizierungsschritten erzeugt ausnutzbare Lücken, die Angreifer nutzen können, um Sicherheitskontrollen vollständig zu umgehen. Selbst scheinbar geringfügige Auslassungen können katastrophale Folgen haben -- wie bei der RADIUS-Authentifizierungsumgehung gesehen, wo fehlende Verifizierung des gemeinsamen Geheimnisses in Antwortpaketen eine vollständige Authentifizierungsumgehung durch gefälschte Server-Antworten ermöglichte. Das Risiko wird verstärkt, da diese Schwachstellen oft in ansonsten gut entworfenen Systemen auftreten, bei denen die Algorithmusauswahl angemessen war. Angreifer, die das Protokoll verstehen, können identifizieren, welcher Schritt übersprungen wurde, und Angriffe gezielt auf diese Lücke ausrichten. Diese Schwachstellen sind besonders gefährlich in Netzwerkprotokollen, wo Angreifer Kommunikation abfangen und ändern können.

Lösung

Implementieren Sie Authentifizierungsalgorithmen exakt wie spezifiziert, ohne Abkürzungen oder Optimierungen, die Schritte überspringen. Führen Sie umfassende Code-Reviews gegen die Algorithmus-Spezifikation durch, um zu verifizieren, dass alle Schritte vorhanden sind. Verwenden Sie etablierte Authentifizierungsbibliotheken, die gründlich geprüft wurden, anstelle benutzerdefinierter Implementierungen. Erstellen Sie Unit-Tests, die verifizieren, dass jeder Schritt des Authentifizierungsprozesses ausgeführt wird und nicht umgangen werden kann. Dokumentieren Sie den erwarteten Authentifizierungsablauf und validieren Sie, dass die Implementierung diesem entspricht. Achten Sie besonders auf Fehlerbehandlungspfade, die Schritte überspringen könnten, wenn Fehler auftreten. Erwägen Sie formale Verifikation für kritische Authentifizierungskomponenten, um Korrektheit mathematisch zu beweisen.

Häufige Auswirkungen

AuswirkungDetails
ZugriffskontrolleBereich: Zugriffskontrolle

Fehlende Authentifizierungsschritte ermöglichen es Angreifern, Schutzmechanismen vollständig zu umgehen und unautorisierten Zugang zu geschützten Ressourcen, Benutzerkonten und Systemfunktionalität zu erhalten.
Integrität, VertraulichkeitBereich: Integrität, Vertraulichkeit

Bei umgangener Authentifizierung können Angreifer die Identität legitimer Benutzer annehmen, unautorisierte Befehle ausführen, sensible Daten lesen und geschützte Informationen ändern.

Beispielcode und Lösung

Verwundbarer Code (Python)

Die folgenden Beispiele zeigen Authentifizierung mit fehlenden kritischen Schritten:

# Verwundbar: RADIUS-artige Authentifizierung ohne Verifizierung des gemeinsamen Geheimnisses
import hashlib
import socket
import struct

class VulnerableRADIUSClient:
    def __init__(self, server, secret):
        self.server = server
        self.secret = secret.encode()
        self.request_authenticator = None

    def authenticate(self, username, password):
        # Schritt 1: Access-Request-Paket erstellen
        request = self.create_access_request(username, password)
        self.request_authenticator = request[4:20]  # Für Verifizierung speichern

        # Schritt 2: Anfrage senden
        sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        sock.sendto(request, (self.server, 1812))

        # Schritt 3: Antwort empfangen
        response, _ = sock.recvfrom(4096)

        # Verwundbar: Kritischer Schritt fehlt!
        # Sollte Response-Authenticator verifizieren = MD5(Code+ID+Length+RequestAuth+Attributes+Secret)
        # Prüft stattdessen nur das Code-Feld

        code = response[0]
        if code == 2:  # Access-Accept
            # Angreifer kann Access-Accept ohne Kenntnis des Geheimnisses fälschen!
            return True
        return False

    def verify_response_authenticator(self, response):
        # Diese Methode existiert, wird aber NIE AUFGERUFEN!
        code = response[0:1]
        identifier = response[1:2]
        length = response[2:4]
        response_auth = response[4:20]
        attributes = response[20:]

        # Korrekte Berechnung
        expected = hashlib.md5(
            code + identifier + length + self.request_authenticator +
            attributes + self.secret
        ).digest()

        return response_auth == expected
// Verwundbar: OAuth-artige Authentifizierung ohne State-Parameter-Verifizierung
public class VulnerableOAuthClient {

    private String clientId;
    private String clientSecret;
    private String redirectUri;

    public String startAuthorization() {
        // Schritt 1: State-Parameter für CSRF-Schutz generieren
        String state = generateRandomState();
        // Verwundbar: State gespeichert, aber nie verifiziert!
        sessionStore.put("oauth_state", state);

        // Schritt 2: Zum Autorisierungsserver weiterleiten
        String authUrl = String.format(
            "https://auth.example.com/authorize?client_id=%s&redirect_uri=%s&state=%s",
            clientId, redirectUri, state
        );

        return authUrl;
    }

    public TokenResponse handleCallback(HttpServletRequest request) {
        String code = request.getParameter("code");
        String returnedState = request.getParameter("state");

        // Verwundbar: Kritischer Schritt fehlt - State-Verifizierung!
        // String expectedState = sessionStore.get("oauth_state");
        // if (!expectedState.equals(returnedState)) {
        //     throw new SecurityException("CSRF detected");
        // }

        // Fährt ohne State-Verifizierung fort, ermöglicht CSRF-Angriffe
        return exchangeCodeForToken(code);
    }

    private TokenResponse exchangeCodeForToken(String code) {
        // Autorisierungscode gegen Access Token tauschen
        // ...
    }
}
// Verwundbar: TLS-artiger Handshake überspringt Zertifikatsverifizierungsschritt
#include <openssl/ssl.h>

typedef struct {
    int step;
    SSL *ssl;
    X509 *peer_cert;
} HandshakeContext;

int vulnerable_handshake(HandshakeContext *ctx) {
    int result;

    // Schritt 1: ClientHello senden
    result = send_client_hello(ctx->ssl);
    if (result != 0) return -1;
    ctx->step = 1;

    // Schritt 2: ServerHello empfangen
    result = receive_server_hello(ctx->ssl);
    if (result != 0) return -1;
    ctx->step = 2;

    // Schritt 3: Zertifikat empfangen
    ctx->peer_cert = receive_certificate(ctx->ssl);
    if (ctx->peer_cert == NULL) return -1;
    ctx->step = 3;

    // Verwundbar: Schritt 4 FEHLT - Zertifikatsverifizierung!
    // Sollte aufrufen: verify_certificate_chain(ctx->peer_cert)
    // Sollte aufrufen: verify_hostname(ctx->peer_cert, expected_host)

    // Schritt 5: Direkt zum Schlüsselaustausch weitergehen
    result = perform_key_exchange(ctx->ssl);
    if (result != 0) return -1;
    ctx->step = 5;

    // Angreifer kann beliebiges Zertifikat vorlegen!
    return 0;
}

Sichere Lösung (Python)

# Behoben: RADIUS-Authentifizierung mit ordnungsgemäßer Verifizierung des gemeinsamen Geheimnisses
import hashlib
import hmac
import socket
import struct

class SecureRADIUSClient:
    def __init__(self, server, secret):
        self.server = server
        self.secret = secret.encode()
        self.request_authenticator = None

    def authenticate(self, username, password):
        # Schritt 1: Access-Request-Paket erstellen
        request = self.create_access_request(username, password)
        self.request_authenticator = request[4:20]

        # Schritt 2: Anfrage senden
        sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        sock.settimeout(5.0)
        sock.sendto(request, (self.server, 1812))

        # Schritt 3: Antwort empfangen
        try:
            response, _ = sock.recvfrom(4096)
        except socket.timeout:
            return False

        # Behoben: Kritischer Schritt - Response-Authenticator verifizieren
        if not self.verify_response_authenticator(response):
            # Antwort ist gefälscht oder beschädigt!
            log_security_event("Invalid RADIUS response authenticator")
            return False

        # Schritt 4: Jetzt sicher den Antwortcode prüfen
        code = response[0]
        if code == 2:  # Access-Accept
            return True
        elif code == 3:  # Access-Reject
            return False
        else:
            log_security_event(f"Unexpected RADIUS response code: {code}")
            return False

    def verify_response_authenticator(self, response):
        """Verifiziert, dass die Antwort vom echten RADIUS-Server stammt."""
        if len(response) < 20:
            return False

        code = response[0:1]
        identifier = response[1:2]
        length = response[2:4]
        response_auth = response[4:20]
        attributes = response[20:]

        # Behoben: Erwarteten Authenticator mit gemeinsamem Geheimnis berechnen
        expected = hashlib.md5(
            code + identifier + length + self.request_authenticator +
            attributes + self.secret
        ).digest()

        # Konstante-Zeit-Vergleich verwenden
        return hmac.compare_digest(response_auth, expected)
// Behoben: OAuth mit ordnungsgemäßer State-Verifizierung
public class SecureOAuthClient {

    private String clientId;
    private String clientSecret;
    private String redirectUri;

    public String startAuthorization(HttpSession session) {
        // Schritt 1: Kryptographisch sicheren State generieren
        String state = generateSecureState();
        session.setAttribute("oauth_state", state);

        // Erstellungszeit für Ablaufprüfung speichern
        session.setAttribute("oauth_state_created", System.currentTimeMillis());

        // Schritt 2: Autorisierungs-URL mit State erstellen
        String authUrl = String.format(
            "https://auth.example.com/authorize?client_id=%s&redirect_uri=%s&state=%s&response_type=code",
            URLEncoder.encode(clientId, "UTF-8"),
            URLEncoder.encode(redirectUri, "UTF-8"),
            URLEncoder.encode(state, "UTF-8")
        );

        return authUrl;
    }

    public TokenResponse handleCallback(HttpServletRequest request, HttpSession session)
            throws SecurityException {

        String code = request.getParameter("code");
        String returnedState = request.getParameter("state");
        String error = request.getParameter("error");

        // Zuerst auf OAuth-Fehler prüfen
        if (error != null) {
            throw new OAuthException("Authorization failed: " + error);
        }

        // Behoben: Kritischer Schritt - State-Parameter verifizieren
        String expectedState = (String) session.getAttribute("oauth_state");
        Long stateCreated = (Long) session.getAttribute("oauth_state_created");

        // State sofort löschen, um Wiederverwendung zu verhindern
        session.removeAttribute("oauth_state");
        session.removeAttribute("oauth_state_created");

        // Prüfen, ob State gesetzt war
        if (expectedState == null) {
            throw new SecurityException("No OAuth state in session - possible CSRF");
        }

        // State-Übereinstimmung prüfen
        if (!MessageDigest.isEqual(
                expectedState.getBytes(), returnedState.getBytes())) {
            throw new SecurityException("State mismatch - CSRF attack detected");
        }

        // Prüfen, ob State nicht zu alt ist (5-Minuten-Limit)
        if (stateCreated == null ||
            System.currentTimeMillis() - stateCreated > 300000) {
            throw new SecurityException("OAuth state expired");
        }

        // Alle Verifizierungen bestanden, Code gegen Token tauschen
        return exchangeCodeForToken(code);
    }

    private String generateSecureState() {
        byte[] bytes = new byte[32];
        new SecureRandom().nextBytes(bytes);
        return Base64.getUrlEncoder().withoutPadding().encodeToString(bytes);
    }
}

Die Behebung stellt sicher, dass alle kritischen Authentifizierungsschritte ohne Auslassungen ausgeführt werden.


Ausgenutzt in der Praxis

RADIUS-Authentifizierungsumgehung (Netzwerkinfrastruktur, 2004)

CVE-2004-2163 dokumentierte eine RADIUS-Client-Implementierung, die das gemeinsame Geheimnis in Antwortpaketen nicht verifizierte, wodurch Angreifer Access-Accept-Antworten fälschen und die Authentifizierung vollständig umgehen könnten.

Protokoll-Initialisierungsumgehung (Verschiedene Anwendungen, 2005)

CVE-2005-3327 dokumentierte eine Authentifizierungsumgehung, bei der das Überspringen des ersten vom Protokoll geforderten Startschritts es Angreifern ermöglichte, Authentifizierungskontrollen zu umgehen.


Tools zum Testen und Ausnutzen

  • Wireshark -- Netzwerkanalysator zur Untersuchung von Authentifizierungsprotokoll-Austauschen.

  • Burp Suite -- Web-Sicherheitstool zum Testen von OAuth- und Web-Authentifizierungsabläufen.

  • FreeRADIUS -- RADIUS-Server mit Debugging-Fähigkeiten für Protokollanalyse.


CVE-Beispiele

  • CVE-2004-2163 -- Gemeinsames RADIUS-Geheimnis nicht in Antwortpaketen verifiziert.

  • CVE-2005-3327 -- Authentifizierungsumgehung durch Überspringen der Protokollinitialisierung.

  • CVE-2014-1266 -- Apple SSL überspringt Signaturverifizierungsschritt.


Referenzen

  1. MITRE Corporation. "CWE-304: Missing Critical Step in Authentication." Common Weakness Enumeration. https://cwe.mitre.org/data/definitions/304.html

  2. RFC 2865. "Remote Authentication Dial In User Service (RADIUS)." https://tools.ietf.org/html/rfc2865

  3. OWASP Foundation. "Authentication Cheat Sheet." https://cheatsheetseries.owasp.org/cheatsheets/Authentication_Cheat_Sheet.html