Unsachgemäßes Befolgen der Vertrauenskette eines Zertifikats

Beschreibung

Unsachgemäßes Befolgen der Vertrauenskette eines Zertifikats ist eine Schwachstelle, die auftritt, wenn ein Produkt die Vertrauenskette für ein Zertifikat nicht oder falsch zurück zu einem vertrauenswürdigen Stammzertifikat verfolgt. Das aus Zertifikaten abgeleitete Vertrauen hängt von der Validierung der gesamten Kette vom Endentitätszertifikat über Zwischenzertifikate zu einer seriösen Stammzertifizierungsstelle ab. Wenn nur das unmittelbare Zertifikat geprüft wird oder Validierungsabkürzungen genommen werden, kann echtes Vertrauen nicht hergestellt werden. Häufige Fehler umfassen das Akzeptieren selbstsignierter Zertifikate in der Kette (außer auf Stammebene), Überspringen der Zwischenzertifikatsvalidierung, Ignorieren fehlender Basic Constraints oder kritischer Erweiterungen und Vertrauen in kompromittierte oder unsachgemäß autorisierte Stammzertifikate.

Risiko

Unsachgemäße Vertrauenskettenvalidierung untergräbt grundlegend zertifikatsbasierte Sicherheit. Angreifer können selbstsignierte Zertifikate erstellen oder Zertifikate von jeder CA (einschließlich kompromittierter) erhalten und sie in Man-in-the-Middle-Angriffen verwenden. DNS-Poisoning kombiniert mit Akzeptanz selbstsignierter Zertifikate ermöglicht Angreifern, Benutzer auf bösartige Server umzuleiten, die vertrauenswürdig erscheinen. Wenn Zwischenzertifikate nicht validiert werden, können Angreifer ihr bösartiges Zertifikat an jede vertrauenswürdige Wurzel ketten, auf die sie verweisen können. Fehlende Basic-Constraints-Validierung ermöglicht Endentitätszertifikaten, unangemessen als CA-Zertifikate zu fungieren und beliebige Zertifikate zu signieren, denen Anwendungen dann vertrauen. Das Risiko wird verstärkt, da diese Fehler typischerweise still auftreten - Benutzer glauben, ihre Verbindungen seien sicher, während sie tatsächlich anfällig für Abfangen sind.

Lösung

Integrieren Sie ordnungsgemäße Zertifikatskettenvalidierung bereits in der Entwurfsphase. Verstehen und implementieren Sie alle Vertrauenskettenverifikationsprüfungen vollständig, einschließlich: Validierung, dass jedes Zertifikat in der Kette vom nächsten signiert ist, Verifikation, dass alle Zertifikate innerhalb ihres Gültigkeitszeitraums sind, Prüfung der Basic-Constraints-Erweiterungen, um sicherzustellen, dass nur CA-Zertifikate andere Zertifikate signieren können, Verifikation, dass Endentitätszertifikate nicht als CAs fungieren, Prüfung des Zertifikatswiderrufsstatus für alle Zertifikate in der Kette, und Sicherstellung, dass die Kette bei einem vertrauenswürdigen Stammzertifikat endet. Bei Verwendung von Certificate Pinning validieren Sie die vollständige Kette vor dem Pinning. Verwenden Sie gut getestete TLS-Bibliotheken, die ordnungsgemäße Kettenvalidierung implementieren, und stellen Sie sicher, dass Sie die Validierungsfunktionen korrekt aufrufen.

Häufige Auswirkungen

AuswirkungDetails
Non-RepudiationBereich: Non-Repudiation

Die Ausnutzung dieses Fehlers ermöglicht das Verstecken bösartiger Aktivitäten hinter scheinbar vertrauenswürdigen Quellen. Daten scheinen von vertrauenswürdigen Entitäten zu stammen, obwohl sie möglicherweise von Angreifern abgefangen und modifiziert wurden.
Zugriffskontrolle, Integrität, VertraulichkeitBereich: Zugriffskontrolle, Integrität, Vertraulichkeit, Verfügbarkeit

Angreifer können privilegierte Aktionen als vertrauenswürdige Entitäten durchführen, unbefugten Zugang zu sensiblen Daten erlangen und die Integrität der Kommunikation kompromittieren.

Beispielcode

Anfälliger Code (C/OpenSSL)

Die folgenden Beispiele demonstrieren unsachgemäße Vertrauenskettenvalidierung:

// Anfällig: Akzeptiert selbstsignierte Zertifikate in der Kette
#include <openssl/ssl.h>
#include <openssl/x509.h>

int vulnerable_verify_callback(int preverify_ok, X509_STORE_CTX *ctx) {
    int err = X509_STORE_CTX_get_error(ctx);

    // Anfällig: Akzeptiert selbstsignierte Zertifikate
    if (err == X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN) {
        return 1;  // Selbstsigniert erlauben - GEFÄHRLICH!
    }

    // Anfällig: Ignoriert Tiefenfehler
    if (err == X509_V_ERR_CERT_CHAIN_TOO_LONG) {
        return 1;  // Kettenlangenvalidierung überspringen
    }

    return preverify_ok;
}

int vulnerable_check_cert(SSL *ssl, const char *host) {
    X509 *cert = SSL_get_peer_certificate(ssl);
    long foo;

    if (cert && host) {
        foo = SSL_get_verify_result(ssl);
    }

    // Anfällig: Akzeptiert selbstsigniert oder Verifikationsfehler
    if ((X509_V_OK == foo) ||
        (X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN == foo)) {
        // Zertifikat "akzeptiert" - aber Kette nicht validiert!
        return 1;
    }

    return 0;
}
# Anfällig: Python ignoriert Kettenvalidierungsfehler
import ssl
import socket

def vulnerable_connect(hostname, port):
    context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)

    # Anfällig: Lädt zusätzliche nicht vertrauenswürdige CAs
    context.load_verify_locations('/path/to/untrusted/ca-bundle.pem')

    # Anfällig: Benutzerdefinierter Verify-Callback der Fehler ignoriert
    # (Python exponiert dies nicht direkt, aber äquivalentes Verhalten)

    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    ssl_sock = context.wrap_socket(sock, server_hostname=hostname)

    try:
        ssl_sock.connect((hostname, port))
    except ssl.SSLCertVerificationError as e:
        # Anfällig: Ignoriert Verifikationsfehler und fährt fort
        if "self signed" in str(e).lower():
            print("Warnung: Selbstsigniertes Zertifikat (ignoriert)")
            # Fährt trotzdem fort!

    return ssl_sock
// Anfällig: Java ignoriert Kettenvalidierung
import javax.net.ssl.*;
import java.security.cert.*;

public class VulnerableChainValidator implements X509TrustManager {

    private final X509TrustManager defaultTm;

    public VulnerableChainValidator() throws Exception {
        TrustManagerFactory tmf = TrustManagerFactory.getInstance("X509");
        tmf.init((KeyStore) null);
        defaultTm = (X509TrustManager) tmf.getTrustManagers()[0];
    }

    @Override
    public void checkServerTrusted(X509Certificate[] chain, String authType)
            throws CertificateException {
        try {
            defaultTm.checkServerTrusted(chain, authType);
        } catch (CertificateException e) {
            // Anfällig: Ignoriert bestimmte Kettenfehler
            if (e.getMessage().contains("self-signed") ||
                e.getMessage().contains("path building")) {
                // Trotzdem akzeptieren!
                return;
            }
            throw e;
        }
    }

    // ... andere Methoden
}

Korrigierter Code (C/OpenSSL)

// Korrigiert: Ordnungsgemäße Zertifikatskettenvalidierung
#include <openssl/ssl.h>
#include <openssl/x509.h>
#include <openssl/x509v3.h>

SSL_CTX* secure_create_context() {
    SSL_CTX *ctx = SSL_CTX_new(TLS_client_method());

    // Vollständige Verifikation aktivieren
    SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);

    // Nur vertrauenswürdige Stamm-CAs laden
    SSL_CTX_set_default_verify_paths(ctx);

    // Ordnungsgemäße Verifikationstiefe setzen
    SSL_CTX_set_verify_depth(ctx, 4);

    // CRL-Prüfung aktivieren
    X509_STORE *store = SSL_CTX_get_cert_store(ctx);
    X509_STORE_set_flags(store, X509_V_FLAG_CRL_CHECK |
                                X509_V_FLAG_CRL_CHECK_ALL);

    return ctx;
}

// Korrigiert: Strikte Verifikations-Callback
int strict_verify_callback(int preverify_ok, X509_STORE_CTX *ctx) {
    if (!preverify_ok) {
        int err = X509_STORE_CTX_get_error(ctx);
        int depth = X509_STORE_CTX_get_error_depth(ctx);
        X509 *cert = X509_STORE_CTX_get_current_cert(ctx);

        char subject[256];
        X509_NAME_oneline(X509_get_subject_name(cert), subject, sizeof(subject));

        fprintf(stderr, "Zertifikatsfehler bei Tiefe %d:\n", depth);
        fprintf(stderr, "  Subject: %s\n", subject);
        fprintf(stderr, "  Fehler: %s\n", X509_verify_cert_error_string(err));

        // Korrigiert: ALLE Validierungsfehler ablehnen
        return 0;
    }

    // Zusätzliche Prüfungen auch wenn preverify bestanden
    X509 *cert = X509_STORE_CTX_get_current_cert(ctx);
    int depth = X509_STORE_CTX_get_error_depth(ctx);

    // Basic Constraints für Zwischenzertifikate prüfen
    if (depth > 0) {
        BASIC_CONSTRAINTS *bc = X509_get_ext_d2i(cert, NID_basic_constraints,
                                                  NULL, NULL);
        if (bc == NULL || !bc->ca) {
            fprintf(stderr, "Zwischenzertifikat ist keine CA\n");
            BASIC_CONSTRAINTS_free(bc);
            return 0;
        }
        BASIC_CONSTRAINTS_free(bc);
    }

    return 1;
}

int secure_check_cert(SSL *ssl, const char *expected_host) {
    // Zertifikat holen
    X509 *cert = SSL_get_peer_certificate(ssl);
    if (cert == NULL) {
        fprintf(stderr, "Kein Zertifikat präsentiert\n");
        return 0;
    }

    // Korrigiert: Verifikationsergebnis prüfen - KEINE Ausnahmen
    long verify_result = SSL_get_verify_result(ssl);
    if (verify_result != X509_V_OK) {
        fprintf(stderr, "Zertifikatsverifikation fehlgeschlagen: %s\n",
                X509_verify_cert_error_string(verify_result));
        X509_free(cert);
        return 0;
    }

    // Korrigiert: Hostname verifizieren
    if (X509_check_host(cert, expected_host, strlen(expected_host),
                        0, NULL) != 1) {
        fprintf(stderr, "Hostname-Verifikation fehlgeschlagen\n");
        X509_free(cert);
        return 0;
    }

    X509_free(cert);
    return 1;  // Vollständig validiert
}
# Korrigiert: Python mit strikter Kettenvalidierung
import ssl
import socket
import certifi

def secure_connect(hostname, port):
    # Standard-Kontext mit strikter Validierung verwenden
    context = ssl.create_default_context(cafile=certifi.where())

    # Sicherstellen, dass alle Validierung aktiviert ist
    context.check_hostname = True
    context.verify_mode = ssl.CERT_REQUIRED

    # Keine zusätzlichen nicht vertrauenswürdigen CAs laden
    # context.load_verify_locations() nur für zusätzliche vertrauenswürdige CAs

    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    ssl_sock = context.wrap_socket(sock, server_hostname=hostname)

    try:
        ssl_sock.connect((hostname, port))
    except ssl.SSLCertVerificationError as e:
        # Korrigiert: Verifikationsfehler nie ignorieren
        print(f"Zertifikatsverifikation fehlgeschlagen: {e}")
        raise  # Fehler propagieren, nicht unterdrücken

    return ssl_sock

Die Korrektur stellt vollständige Kettenvalidierung ohne Ausnahmen für selbstsignierte Zertifikate sicher, validiert Basic Constraints, prüft Hostname und lehnt alle Verifikationsfehler ab.


Ausgenutzt in der Praxis

Selbstsignierte Zertifikatsakzeptanz (Verschiedene Anwendungen, Fortlaufend)

CVE-2008-4989 dokumentierte die Akzeptanz selbstsignierter Zertifikatsketten, was Man-in-the-Middle-Angriffe ermöglichte, bei denen Angreifer selbstsignierte Zertifikate präsentierten, denen Anwendungen fälschlicherweise vertrauten.

Nicht vertrauenswürdige CA in Kette (Android-Apps, 2016)

CVE-2016-2402 dokumentierte Umgehung via nicht vertrauenswürdiger CA-Zertifikate in der Kette, wobei Anwendungen nicht validierten, dass alle CAs in der Kette vertrauenswürdig waren.

Fehlende Basic-Constraints-Validierung (Browser, Historisch)

CVE-2002-0970 und CVE-2002-0862 dokumentierten Webbrowser, die Basic-Constraints-Erweiterungen nicht validierten, was Endentitätszertifikaten ermöglichte, als CAs zu fungieren und beliebige Zertifikate zu signieren.


Tools zum Testen/Ausnutzen

  • testssl.sh — Kommandozeilentool zur Prüfung von SSL/TLS-Konfigurationen einschließlich Kettenvalidierung.

  • mitmproxy — HTTPS-Proxy zum Testen der Zertifikatskettenvalidierung.

  • openssl s_client — Tool zum Testen von SSL/TLS-Verbindungen und Zertifikatsketten.


CVE-Beispiele

  • CVE-2016-2402 — Umgehung via nicht vertrauenswürdiger CA in Zertifikatskette.

  • CVE-2008-4989 — Selbstsignierte Zertifikatskettenakzeptanz.

  • CVE-2012-5821 — Fehlerhafte TLS-Funktionsverwendung verhindert CA-Verifikation.

  • CVE-2009-3046 — Fehlende Widerrufsprüfungen auf Zwischenzertifikaten.

  • CVE-2002-0970 — Fehlende Basic-Constraints-Validierung.


Referenzen

  1. MITRE Corporation. "CWE-296: Improper Following of a Certificate's Chain of Trust." Common Weakness Enumeration. https://cwe.mitre.org/data/definitions/296.html

  2. RFC 5280. "Internet X.509 Public Key Infrastructure Certificate and Certificate Revocation List (CRL) Profile." https://tools.ietf.org/html/rfc5280

  3. OWASP Foundation. "Transport Layer Protection Cheat Sheet." https://cheatsheetseries.owasp.org/cheatsheets/Transport_Layer_Protection_Cheat_Sheet.html