Unsachgemäße Validierung des Zertifikatsablaufs
Beschreibung
Unsachgemäße Validierung des Zertifikatsablaufs ist eine Schwachstelle, die auftritt, wenn Zertifikatsablaufdaten nicht validiert werden oder falsch validiert werden, sodass Zertifikaten, die aufgrund ihres Alters aufgegeben wurden, Vertrauen zugewiesen werden kann. Zertifikate enthalten Ablaufdaten als Sicherheitsmechanismus, um das Zeitfenster zu begrenzen, in dem ein kompromittiertes Zertifikat verwendet werden kann. Wenn Anwendungen Ablaufdaten nicht prüfen oder ignorieren, akzeptieren sie möglicherweise Zertifikate, denen nicht mehr vertraut werden sollte. Im Laufe der Zeit werden abgelaufene Zertifikate zunehmend riskant, da die Wahrscheinlichkeit steigt, dass sie kompromittiert wurden oder ihre privaten Schlüssel offengelegt wurden.
Risiko
Das Akzeptieren abgelaufener Zertifikate untergräbt eine grundlegende Sicherheitskontrolle im Zertifikatslebenszyklus. Mit zunehmendem Alter von Zertifikaten über ihr Ablaufdatum hinaus steigt das Kompromittierungsrisiko durch verschiedene Vektoren: Private Schlüssel können geleakt worden sein, kryptografische Algorithmen können geschwächt worden sein, die Umstände des Zertifikatsinhabers können sich geändert haben, oder Widerrufsinformationen sind möglicherweise nicht mehr verfügbar. Abgelaufene Zertifikate sind besonders gefährlich, weil Organisationen möglicherweise aufhören, ihre privaten Schlüssel zu schützen, sobald Zertifikate ablaufen, in der Annahme, dass sie nicht mehr verwendet werden. Angreifer, die private Schlüssel abgelaufener Zertifikate erhalten, können sie gegen Anwendungen verwenden, die den Ablauf nicht validieren. Der Sicherheitsvorteil der zertifikatsbasierten Authentifizierung wird erheblich reduziert, wenn der Ablauf nicht durchgesetzt wird.
Lösung
Implementieren Sie Prüfungen für Zertifikatsablaufdaten und lehnen Sie abgelaufene Zertifikate ab. Verwenden Sie TLS-Bibliotheksfunktionen, die Ablaufprüfung als Teil der Standardvalidierung einschließen. Fügen Sie nie Ausnahmen für abgelaufene Zertifikate in Produktionscode hinzu. Informieren Sie Benutzer über Zertifikatsablaufprobleme und stellen Sie Korrekturschritte bereit, wenn Zertifikate kurz vor dem Ablauf stehen. Überwachen Sie Zertifikatsablaufdaten proaktiv und erneuern Sie vor dem Ablauf. Bei Implementierung von Certificate Pinning validieren Sie alle Zertifikatseigenschaften einschließlich Ablauf vor dem Pinning. Implementieren Sie automatisierte Zertifikatserneuerungsprozesse, um zu verhindern, dass legitime Dienste abgelaufene Zertifikate präsentieren. Erwägen Sie die Implementierung von Warnungen für Zertifikate, die sich dem Ablauf nähern.
Häufige Auswirkungen
| Auswirkung | Details |
|---|---|
| Integrität, Authentifizierung | Bereich: Integrität, Authentifizierung Daten von Systemen, die abgelaufene Zertifikate verwenden, können durch bösartiges Spoofing korrumpiert werden. Abgelaufene Zertifikate können Angreifern, die den privaten Schlüssel des Zertifikats erhalten haben, ermöglichen, vertrauenswürdige Systeme zu imitieren, Kommunikation abzufangen und Daten während der Übertragung zu modifizieren. |
Beispielcode
Anfälliger Code (C/OpenSSL)
Die folgenden Beispiele demonstrieren unsachgemäße Zertifikatsablaufvalidierung:
// Anfällig: Akzeptiert abgelaufene Zertifikate
#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 abgelaufene Zertifikate
if (err == X509_V_ERR_CERT_HAS_EXPIRED) {
return 1; // Abgelaufen akzeptieren - GEFÄHRLICH!
}
// Auch anfällig: Akzeptiert noch-nicht-gültige Zertifikate
if (err == X509_V_ERR_CERT_NOT_YET_VALID) {
return 1; // Zukünftig datierte Zertifikate akzeptieren
}
return preverify_ok;
}
int vulnerable_check_cert(SSL *ssl) {
X509 *cert = SSL_get_peer_certificate(ssl);
long result = SSL_get_verify_result(ssl);
// Anfällig: Ignoriert Ablaufsfehler
if ((result == X509_V_OK) ||
(result == X509_V_ERR_CERT_HAS_EXPIRED)) {
// Behandelt abgelaufen als gültig!
return 1;
}
return 0;
}
# Anfällig: Python ignoriert Ablauf
import ssl
import socket
from datetime import datetime
def vulnerable_connect(hostname, port):
context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
context.verify_mode = ssl.CERT_REQUIRED
context.load_default_certs()
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 Ablaufsfehler
if "certificate has expired" in str(e).lower():
print("Warnung: Zertifikat abgelaufen (ignoriert)")
# Erstellt neuen Socket ohne Verifikation
context2 = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
context2.check_hostname = False
context2.verify_mode = ssl.CERT_NONE
sock2 = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
ssl_sock = context2.wrap_socket(sock2)
ssl_sock.connect((hostname, port))
return ssl_sock
// Anfällig: Java ignoriert Ablauf
import javax.net.ssl.*;
import java.security.cert.*;
import java.util.Date;
public class VulnerableExpirationCheck implements X509TrustManager {
private final X509TrustManager defaultTm;
public VulnerableExpirationCheck() 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 (CertificateExpiredException e) {
// Anfällig: Ignoriert Ablauf!
System.out.println("Warnung: Zertifikat abgelaufen, fahre trotzdem fort");
// Wirft nicht erneut - akzeptiert abgelaufenes Zertifikat
}
}
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType) {}
@Override
public X509Certificate[] getAcceptedIssuers() {
return defaultTm.getAcceptedIssuers();
}
}
Korrigierter Code (C/OpenSSL)
// Korrigiert: Ordnungsgemäße Zertifikatsablaufvalidierung
#include <openssl/ssl.h>
#include <openssl/x509.h>
#include <time.h>
// 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: Abgelaufene Zertifikate ablehnen
if (err == X509_V_ERR_CERT_HAS_EXPIRED) {
fprintf(stderr, " Zertifikat ist abgelaufen - abgelehnt\n");
return 0; // Ablehnen
}
// Korrigiert: Noch-nicht-gültige Zertifikate ablehnen
if (err == X509_V_ERR_CERT_NOT_YET_VALID) {
fprintf(stderr, " Zertifikat noch nicht gültig - abgelehnt\n");
return 0; // Ablehnen
}
return 0; // Alle Fehler ablehnen
}
return 1;
}
int secure_check_cert(SSL *ssl, const char *hostname) {
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 für Ablauf
long result = SSL_get_verify_result(ssl);
if (result != X509_V_OK) {
fprintf(stderr, "Zertifikatsverifikation fehlgeschlagen: %s\n",
X509_verify_cert_error_string(result));
X509_free(cert);
return 0;
}
// Zusätzliche manuelle Ablaufprüfung
time_t now = time(NULL);
if (X509_cmp_time(X509_get0_notBefore(cert), &now) > 0) {
fprintf(stderr, "Zertifikat noch nicht gültig\n");
X509_free(cert);
return 0;
}
if (X509_cmp_time(X509_get0_notAfter(cert), &now) < 0) {
fprintf(stderr, "Zertifikat ist abgelaufen\n");
X509_free(cert);
return 0;
}
// Hostname verifizieren
if (X509_check_host(cert, hostname, strlen(hostname), 0, NULL) != 1) {
fprintf(stderr, "Hostname-Verifikation fehlgeschlagen\n");
X509_free(cert);
return 0;
}
X509_free(cert);
return 1; // Vollständig validiert einschließlich Ablauf
}
# Korrigiert: Python mit ordnungsgemäßer Ablaufprüfung
import ssl
import socket
import certifi
from datetime import datetime
def secure_connect(hostname, port):
# Standard-Kontext verwenden, der Ablauf validiert
context = ssl.create_default_context(cafile=certifi.where())
context.check_hostname = True
context.verify_mode = ssl.CERT_REQUIRED
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: KEINE Verifikationsfehler ignorieren einschließlich Ablauf
print(f"Zertifikatsverifikation fehlgeschlagen: {e}")
raise # Fehler propagieren, nicht unterdrücken
# Zusätzliche Prüfung: warnen wenn Zertifikat bald abläuft
cert = ssl_sock.getpeercert()
not_after = datetime.strptime(cert['notAfter'], '%b %d %H:%M:%S %Y %Z')
days_until_expiry = (not_after - datetime.utcnow()).days
if days_until_expiry < 30:
print(f"Warnung: Zertifikat läuft in {days_until_expiry} Tagen ab")
return ssl_sock
// Korrigiert: Java mit ordnungsgemäßer Ablaufprüfung
import javax.net.ssl.*;
import java.security.cert.*;
import java.util.Date;
public class SecureExpirationCheck {
public static SSLContext createSecureContext() throws Exception {
// Standard-TrustManager verwenden, der Ablauf validiert
TrustManagerFactory tmf = TrustManagerFactory.getInstance(
TrustManagerFactory.getDefaultAlgorithm());
tmf.init((KeyStore) null);
SSLContext ctx = SSLContext.getInstance("TLS");
ctx.init(null, tmf.getTrustManagers(), null);
// Standard-TrustManagers lehnen abgelaufene Zertifikate ab
return ctx;
}
public static void verifyNotExpired(X509Certificate cert)
throws CertificateExpiredException, CertificateNotYetValidException {
// Korrigiert: Explizite Ablaufprüfung
cert.checkValidity(); // Wirft wenn abgelaufen oder noch nicht gültig
}
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)) {
// Hostname-Verifikation aktivieren
SSLParameters params = socket.getSSLParameters();
params.setEndpointIdentificationAlgorithm("HTTPS");
socket.setSSLParameters(params);
socket.startHandshake();
// Korrigiert: Zusätzliche manuelle Prüfung nach Handshake
SSLSession session = socket.getSession();
X509Certificate[] certs = (X509Certificate[]) session.getPeerCertificates();
for (X509Certificate cert : certs) {
// Wirft CertificateExpiredException wenn abgelaufen
verifyNotExpired(cert);
}
// Zertifikatskette validiert und nicht abgelaufen
}
}
}
Die Korrektur stellt sicher, dass Zertifikatsablauf immer validiert wird und abgelaufene Zertifikate abgelehnt werden.
Ausgenutzt in der Praxis
Akzeptanz abgelaufener Zertifikate (Verschiedene Anwendungen, Fortlaufend)
Anwendungen, die abgelaufene Zertifikate akzeptieren, wurden von Angreifern ausgenutzt, die die privaten Schlüssel abgelaufener Zertifikate erhielten, entweder durch Diebstahl, Kompromittierung von Systemen, die Schlüssel nach Ablauf nicht mehr schützten, oder durch Kauf von Domains mit abgelaufenen Zertifikaten.
Abgelaufene CA-Zertifikate (PKI-Infrastruktur, Historisch)
Abgelaufene Zwischen- oder Stamm-CA-Zertifikate, denen Anwendungen noch vertrauten, ermöglichten Angreifern, weiterhin Zertifikate zu verwenden, die von diesen CAs signiert wurden, selbst nach beabsichtigtem Widerruf durch Ablauf.
Tools zum Testen/Ausnutzen
-
testssl.sh — Kommandozeilentool zum Testen von SSL/TLS einschließlich Zertifikatsablauf.
-
openssl s_client — Tool zum Testen von SSL/TLS-Verbindungen und Anzeigen von Zertifikatsdaten.
-
badssl.com/expired — Testseite mit abgelaufenem Zertifikat zum Testen.
CVE-Beispiele
Zertifikatsablaufvalidierungsfehler werden typischerweise als Teil breiterer Zertifikatsvalidierungsprobleme (CWE-295) dokumentiert anstatt eigenständige CVEs zu erhalten.
Referenzen
-
MITRE Corporation. "CWE-298: Improper Validation of Certificate Expiration." Common Weakness Enumeration. https://cwe.mitre.org/data/definitions/298.html
-
RFC 5280. "Internet X.509 Public Key Infrastructure Certificate and Certificate Revocation List (CRL) Profile." https://tools.ietf.org/html/rfc5280
-
OWASP Foundation. "Transport Layer Protection Cheat Sheet." https://cheatsheetseries.owasp.org/cheatsheets/Transport_Layer_Protection_Cheat_Sheet.html