Unsachgemäße Prüfung auf Zertifikatswiderruf
Beschreibung
Unsachgemäße Prüfung auf Zertifikatswiderruf ist eine Schwachstelle, die auftritt, wenn ein Produkt den Widerrufsstatus eines Zertifikats nicht prüft oder falsch prüft, was dazu führen kann, dass ein kompromittiertes Zertifikat verwendet wird. Zertifikatswiderruf ist ein kritischer Sicherheitsmechanismus, der es Zertifizierungsstellen ermöglicht, Zertifikate vor ihrem natürlichen Ablauf zu invalidieren, wenn der private Schlüssel des Zertifikats kompromittiert wurde, das Zertifikat fehlerhaft ausgestellt wurde oder dem Zertifikatsinhaber nicht mehr vertraut wird. Die Verwendung eines widerrufenen Zertifikats deutet fast sicher auf bösartige Aktivitäten hin, da legitime Server niemals widerrufene Zertifikate präsentieren sollten, es sei denn, sie sind schwer kompromittiert oder veraltet.
Risiko
Das Versäumnis, den Zertifikatswiderruf zu prüfen, stellt eine besonders schwere Schwachstelle dar, da widerrufene Zertifikate typischerweise mit bestätigten Sicherheitsvorfällen verbunden sind. Wenn ein Zertifikat widerrufen wird, bedeutet dies normalerweise, dass der private Schlüssel kompromittiert wurde - möglicherweise durch Datenleck, Serverkompromittierung oder andere Sicherheitsvorfälle. Legitime Server sollten niemals widerrufene Zertifikate verwenden, daher deutet das Auftreten eines solchen stark auf einen falsch konfigurierten legitimen Server oder einen aktiven Angriff hin. Das Risiko wird durch die Tatsache verstärkt, dass Zertifikatswiderrufslisten (CRLs) und das Online Certificate Status Protocol (OCSP) speziell zum Schutz vor kompromittierten Zertifikaten existieren, was deren Umgehung hochgefährlich macht. Router- und Geräte-Firmware, die Schlüssel zwischenspeichert, ohne spätere Widerrufe zu prüfen, ermöglicht Angreifern, kompromittierte Zertifikate lange nach dem Widerruf weiter zu verwenden.
Lösung
Stellen Sie sicher, dass alle Zertifikate einer Widerrufsstatus-Validierung unterzogen werden, bevor ihnen vertraut wird. Implementieren Sie CRL-Prüfung (Certificate Revocation List), um die Liste widerrufener Zertifikate der CA herunterzuladen und dagegen zu verifizieren. Alternativ oder zusätzlich implementieren Sie OCSP (Online Certificate Status Protocol) für Echtzeit-Widerrufsprüfung. Erwägen Sie OCSP-Stapling, bei dem der Server eine signierte, mit Zeitstempel versehene OCSP-Antwort zusammen mit dem Zertifikat bereitstellt, um Latenz und Datenschutzbedenken zu reduzieren. Bei der Implementierung von Certificate Pinning validieren Sie alle Zertifikatseigenschaften einschließlich Widerrufsstatus vollständig, bevor das Pinning erfolgt. Behandeln Sie Fälle, in denen Widerrufsinformationen nicht verfügbar sind - betrachten Sie dies je nach Sicherheitsanforderungen als Fehler. Cachen Sie den Widerrufsstatus angemessen unter Berücksichtigung der Aktualitätsanforderungen.
Häufige Auswirkungen
| Auswirkung | Details |
|---|---|
| Zugriffskontrolle | Bereich: Zugriffskontrolle Nicht vertrauenswürdige Entitäten können Vertrauensbezeichnung erhalten, wenn ihre Zertifikate aufgrund von Widerruf hätten abgelehnt werden sollen. Dies ermöglicht Identitätsfälschung mit kompromittierten Zertifikaten. |
| Integrität, Vertraulichkeit | Bereich: Integrität, Vertraulichkeit Daten von bösartigen Quellen, die widerrufene Zertifikate verwenden, können in vertrauenswürdige Systeme integriert werden. Angreifer, die sich mit widerrufenen Zertifikaten als vertrauenswürdige Entitäten ausgeben, können auf sensible Daten zugreifen. |
Beispielcode
Anfälliger Code (C/OpenSSL)
Die folgenden Beispiele demonstrieren unsachgemäße Widerrufsprüfung:
// Anfällig: Keine Widerrufsprüfung
#include <openssl/ssl.h>
#include <openssl/x509.h>
int vulnerable_verify_cert(SSL *ssl) {
X509 *cert = SSL_get_peer_certificate(ssl);
if (cert) {
// Anfällig: Prüft nur ob Zertifikat existiert, keine Verifikation
// Kein Aufruf von SSL_get_verify_result()
// Keine CRL- oder OCSP-Prüfung
return 1; // "Vertrauenswürdig" - aber könnte widerrufen sein!
}
return 0;
}
SSL_CTX* vulnerable_create_context() {
SSL_CTX *ctx = SSL_CTX_new(TLS_client_method());
// Grundlegende Verifikation aktiviert
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);
SSL_CTX_set_default_verify_paths(ctx);
// Anfällig: Keine CRL-Prüfung konfiguriert
// Keine OCSP-Prüfung konfiguriert
return ctx;
}
# Anfällig: Python ohne Widerrufsprüfung
import ssl
import socket
def vulnerable_connect(hostname, port):
# Pythons Standard-SSL-Kontext prüft keinen Widerruf
context = ssl.create_default_context()
# Keine CRL- oder OCSP-Prüfung
# ssl-Modul hat begrenzte eingebaute Widerrufsunterstützung
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
ssl_sock = context.wrap_socket(sock, server_hostname=hostname)
ssl_sock.connect((hostname, port))
# Zertifikatskette validiert, aber Widerruf NICHT geprüft!
return ssl_sock
// Anfällig: Java ohne Widerrufsprüfung
import javax.net.ssl.*;
import java.security.*;
public class VulnerableRevocationCheck {
public static SSLContext createVulnerableContext() throws Exception {
SSLContext ctx = SSLContext.getInstance("TLS");
// Standard-TrustManager - keine Widerrufsprüfung aktiviert
TrustManagerFactory tmf = TrustManagerFactory.getInstance("X509");
tmf.init((KeyStore) null);
ctx.init(null, tmf.getTrustManagers(), null);
// Anfällig: Widerrufsprüfung nicht aktiviert
// PKIXParameters.setRevocationEnabled(false) ist Standardverhalten
// für viele Konfigurationen
return ctx;
}
}
Korrigierter Code (C/OpenSSL)
// Korrigiert: Ordnungsgemäße Widerrufsprüfung mit CRL
#include <openssl/ssl.h>
#include <openssl/x509.h>
#include <openssl/x509v3.h>
SSL_CTX* secure_create_context_with_crl() {
SSL_CTX *ctx = SSL_CTX_new(TLS_client_method());
// Verifikation aktivieren
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);
SSL_CTX_set_default_verify_paths(ctx);
// Korrigiert: CRL-Prüfung aktivieren
X509_STORE *store = SSL_CTX_get_cert_store(ctx);
// CRL-Prüfung für alle Zertifikate in der Kette aktivieren
X509_STORE_set_flags(store, X509_V_FLAG_CRL_CHECK |
X509_V_FLAG_CRL_CHECK_ALL);
// CRL-Datei laden (sollte regelmäßig aktualisiert werden)
X509_LOOKUP *lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file());
if (lookup) {
X509_LOOKUP_load_file(lookup, "/etc/ssl/crl/ca-crl.pem", X509_FILETYPE_PEM);
}
return ctx;
}
// Korrigiert: OCSP-Prüfungs-Callback
int ocsp_verify_callback(SSL *ssl, void *arg) {
// OCSP-Antwort vom Server holen (gestapelt)
const unsigned char *resp_der;
long resp_len = SSL_get_tlsext_status_ocsp_resp(ssl, &resp_der);
if (resp_len <= 0) {
// Keine OCSP-Antwort bereitgestellt - je nach Richtlinie entscheiden
// Für strenge Sicherheit ablehnen
fprintf(stderr, "Keine OCSP-Antwort erhalten\n");
return 0; // Ablehnen wenn OCSP erforderlich
}
// OCSP-Antwort parsen und verifizieren
OCSP_RESPONSE *resp = d2i_OCSP_RESPONSE(NULL, &resp_der, resp_len);
if (resp == NULL) {
fprintf(stderr, "OCSP-Antwort könnte nicht geparst werden\n");
return 0;
}
int status = OCSP_response_status(resp);
if (status != OCSP_RESPONSE_STATUS_SUCCESSFUL) {
fprintf(stderr, "OCSP-Antwortstatus: %d\n", status);
OCSP_RESPONSE_free(resp);
return 0;
}
// Zusätzliche Verifikation der OCSP-Antwort...
// Signatur prüfen, Zertifikatsstatus prüfen, usw.
OCSP_RESPONSE_free(resp);
return 1;
}
SSL_CTX* secure_create_context_with_ocsp() {
SSL_CTX *ctx = SSL_CTX_new(TLS_client_method());
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);
SSL_CTX_set_default_verify_paths(ctx);
// Korrigiert: OCSP-Stapling-Verifikation aktivieren
SSL_CTX_set_tlsext_status_type(ctx, TLSEXT_STATUSTYPE_ocsp);
SSL_CTX_set_tlsext_status_cb(ctx, ocsp_verify_callback);
return ctx;
}
int secure_verify_cert(SSL *ssl, const char *hostname) {
X509 *cert = SSL_get_peer_certificate(ssl);
if (cert == NULL) {
return 0;
}
// Grundlegende Verifikation prüfen
long result = SSL_get_verify_result(ssl);
if (result != X509_V_OK) {
// Dies schließt X509_V_ERR_CERT_REVOKED ein wenn CRL-Prüfung aktiviert
if (result == X509_V_ERR_CERT_REVOKED) {
fprintf(stderr, "SICHERHEIT: Zertifikat wurde WIDERRUFEN\n");
}
fprintf(stderr, "Zertifikatsverifikation fehlgeschlagen: %s\n",
X509_verify_cert_error_string(result));
X509_free(cert);
return 0;
}
// Hostname-Verifikation
if (X509_check_host(cert, hostname, strlen(hostname), 0, NULL) != 1) {
X509_free(cert);
return 0;
}
X509_free(cert);
return 1;
}
// Korrigiert: Java mit aktivierter Widerrufsprüfung
import javax.net.ssl.*;
import java.security.*;
import java.security.cert.*;
public class SecureRevocationCheck {
public static SSLContext createSecureContext() throws Exception {
// PKIXParameters mit Widerrufsprüfung erstellen
TrustManagerFactory tmf = TrustManagerFactory.getInstance("PKIX");
// System-Truststore holen
KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
trustStore.load(null, null); // Leer initialisieren, verwendet System-Zertifikate
// PKIX-Parameter mit Widerruf konfigurieren
PKIXBuilderParameters pkixParams = new PKIXBuilderParameters(
trustStore, new X509CertSelector());
// Korrigiert: Widerrufsprüfung aktivieren
pkixParams.setRevocationEnabled(true);
// CRL oder OCSP konfigurieren
// Option 1: OCSP aktivieren
Security.setProperty("ocsp.enable", "true");
// Option 2: CRL konfigurieren
System.setProperty("com.sun.security.enableCRLDP", "true");
// Manager-Factory-Parameter erstellen
ManagerFactoryParameters mfp = new CertPathTrustManagerParameters(pkixParams);
tmf.init(mfp);
SSLContext ctx = SSLContext.getInstance("TLS");
ctx.init(null, tmf.getTrustManagers(), null);
return ctx;
}
public static void secureConnect(String hostname, int port) throws Exception {
SSLContext ctx = createSecureContext();
SSLSocketFactory factory = ctx.getSocketFactory();
try (SSLSocket socket = (SSLSocket) factory.createSocket(hostname, port)) {
SSLParameters params = socket.getSSLParameters();
params.setEndpointIdentificationAlgorithm("HTTPS");
socket.setSSLParameters(params);
socket.startHandshake();
// Wenn wir hier ankommen, ist das Zertifikat gültig UND nicht widerrufen
// (CertPathValidatorException wird geworfen wenn widerrufen)
} catch (SSLHandshakeException e) {
if (e.getCause() instanceof CertPathValidatorException) {
CertPathValidatorException cpve = (CertPathValidatorException) e.getCause();
if (cpve.getReason() == CertPathValidatorException.BasicReason.REVOKED) {
System.err.println("SICHERHEIT: Zertifikat wurde WIDERRUFEN");
}
}
throw e;
}
}
}
Die Korrektur aktiviert CRL- und/oder OCSP-Prüfung, um den Zertifikatswiderrufsstatus zu verifizieren, bevor Zertifikaten vertraut wird.
Ausgenutzt in der Praxis
LDAP-über-SSL-Widerrufsumgehung (Enterprise-Systeme, 2011)
CVE-2011-2014 dokumentierte LDAP-über-SSL-Implementierungen, die Certificate Revocation Lists nicht prüften, was die weitere Verwendung widerrufener Zertifikate für Authentifizierung ermöglichte.
Browser-Zwischenzertifikat-Widerruf (Webbrowser, 2009)
CVE-2009-3046 dokumentierte Webbrowser, die den Widerruf für Zwischenzertifikate nicht prüften, sondern nur für Endentitätszertifikate, was Angriffe mit widerrufenen CA-Zertifikaten ermöglichte.
Router-Schlüssel-Caching (Netzwerkgeräte, 2011)
CVE-2011-0935 dokumentierte Router, die Zertifikatsschlüssel zwischenspeicherten, ohne auf späteren Widerruf zu prüfen, was die weitere Verwendung widerrufener Zertifikate ermöglichte.
Tools zum Testen/Ausnutzen
-
testssl.sh — Kommandozeilentool zum Testen von SSL/TLS einschließlich OCSP- und CRL-Prüfung.
-
openssl ocsp — Tool zum Testen von OCSP-Antworten.
-
revoked.badssl.com — Testseite mit widerrufenem Zertifikat zum Testen.
CVE-Beispiele
-
CVE-2011-2014 — LDAP-über-SSL prüfte Certificate Revocation Lists nicht.
-
CVE-2009-3046 — Webbrowser prüfte Zwischenzertifikat-Widerruf nicht.
-
CVE-2011-0935 — Router cachte Schlüssel und umging spätere Widerrufsprüfungen.
-
CVE-2006-4409 — Widerrufsliste über HTTP-Proxies nicht erreichbar.
Referenzen
-
MITRE Corporation. "CWE-299: Improper Check for Certificate Revocation." Common Weakness Enumeration. https://cwe.mitre.org/data/definitions/299.html
-
RFC 5280. "Internet X.509 Public Key Infrastructure Certificate and Certificate Revocation List (CRL) Profile." https://tools.ietf.org/html/rfc5280
-
RFC 6960. "X.509 Internet Public Key Infrastructure Online Certificate Status Protocol - OCSP." https://tools.ietf.org/html/rfc6960