Fehlende Validierung des OpenSSL-Zertifikats
Beschreibung
Fehlende Validierung des OpenSSL-Zertifikats tritt auf, wenn eine Anwendung, die OpenSSL verwendet, SSL/TLS-Zertifikate während sicherer Verbindungen nicht ordnungsgemäß verifiziert. Dies umfasst das Versäumnis, die Zertifikatskette zu prüfen, nicht zu verifizieren, dass der Hostname mit dem Common Name oder den Subject Alternative Names des Zertifikats übereinstimmt, das Akzeptieren abgelaufener Zertifikate oder das Nicht-Prüfen des Zertifikats-Widerrufsstatus. Die Anwendung stellt Verbindungen ohne ordnungsgemäße Identitätsverifikation her.
Risiko
Ohne Zertifikatsvalidierung können Angreifer Man-in-the-Middle-Angriffe mit selbstsignierten oder unsachgemäß ausgestellten Zertifikaten durchführen. Verbindungen, die als sicher angenommen werden, bieten tatsächlich keine Authentifizierung des entfernten Servers. Sensible Daten (Credentials, persönliche Informationen, Finanzdaten), die über diese Verbindungen übertragen werden, können abgefangen und gelesen werden. Die Anwendung kann sich mit bösartigen Servern verbinden, die legitime Dienste imitieren.
Lösung
Aktivieren Sie Zertifikatsverifizierung mit SSL_CTX_set_verify() und SSL_VERIFY_PEER. Laden Sie vertrauenswürdige CA-Zertifikate mit SSL_CTX_load_verify_locations(). Verifizieren Sie, dass der Hostname mit dem Zertifikat übereinstimmt, mit SSL_set1_host() oder manueller Verifikation. Prüfen Sie SSL_get_verify_result() nach der Verbindung. Erwägen Sie die Prüfung des Zertifikats-Widerrufsstatus via OCSP oder CRLs. Verwenden Sie moderne TLS-Versionen und deaktivieren Sie unsichere Protokolle.
Häufige Auswirkungen
| Auswirkung | Details |
|---|---|
| Vertraulichkeit | Bereich: Datenabfangen Angreifer können verschlüsselte Kommunikation abfangen. |
| Authentifizierung | Bereich: Server-Imitation Kann nicht verifizieren, dass mit beabsichtigtem Server verbunden. |
| Integrität | Bereich: Datenmanipulation MITM-Angreifer können Daten während der Übertragung modifizieren. |
Beispielcode + Lösungscode
Verwundbarer Code
// VERWUNDBAR: Keine Zertifikatsverifizierung
SSL_CTX* create_context_vulnerable() {
SSL_CTX* ctx = SSL_CTX_new(TLS_client_method());
// Keine Verifizierung gesetzt - akzeptiert JEDES Zertifikat!
// SSL_CTX_set_verify nicht aufgerufen
return ctx;
}
void connect_vulnerable(const char* host, int port) {
SSL_CTX* ctx = create_context_vulnerable();
SSL* ssl = SSL_new(ctx);
int fd = create_socket(host, port);
SSL_set_fd(ssl, fd);
// Verbindet ohne Server-Identität zu verifizieren!
if (SSL_connect(ssl) <= 0) {
handle_error();
return;
}
// MITM-Angreifer kann jedes Zertifikat präsentieren
send_sensitive_data(ssl);
}
// VERWUNDBAR: Verifizierung deaktiviert
SSL_CTX* create_context_insecure() {
SSL_CTX* ctx = SSL_CTX_new(TLS_client_method());
// Deaktiviert explizit Verifizierung!
SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, NULL);
return ctx;
}
// VERWUNDBAR: Verifizierung aktiviert aber keine CA geladen
SSL_CTX* create_context_no_ca() {
SSL_CTX* ctx = SSL_CTX_new(TLS_client_method());
// Verifizierung angefordert, wird aber immer fehlschlagen
// weil keine vertrauenswürdigen CAs geladen sind
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);
// Fehlt: SSL_CTX_load_verify_locations()
return ctx;
}
// VERWUNDBAR: Keine Hostname-Verifizierung
void connect_no_hostname_check(SSL_CTX* ctx, const char* host, int port) {
SSL* ssl = SSL_new(ctx);
int fd = create_socket(host, port);
SSL_set_fd(ssl, fd);
if (SSL_connect(ssl) > 0) {
// Zertifikat könnte gültig sein, aber für anderen Host!
// Angreifer mit jedem gültigen Zertifikat kann abfangen
if (SSL_get_verify_result(ssl) == X509_V_OK) {
// FEHLER: Hat nicht verifiziert, dass Hostname mit Zertifikat übereinstimmt
send_data(ssl);
}
}
}
// VERWUNDBAR: Verifizierungsergebnis ignorieren
void connect_ignore_result(SSL_CTX* ctx, const char* host, int port) {
SSL* ssl = SSL_new(ctx);
int fd = create_socket(host, port);
SSL_set_fd(ssl, fd);
SSL_connect(ssl); // Kann Verifizierung fehlschlagen
// FEHLER: Hat nie SSL_get_verify_result() geprüft!
send_sensitive_data(ssl);
}
# VERWUNDBAR: Python mit deaktivierter Zertifikatsverifizierung
import ssl
import urllib.request
# VERWUNDBAR: Alle Zertifikatsprüfungen deaktivieren
context = ssl.create_default_context()
context.check_hostname = False
context.verify_mode = ssl.CERT_NONE
response = urllib.request.urlopen(url, context=context)
# VERWUNDBAR: Verwendung veralteter/unsicherer Methoden
import requests
# Deaktiviert Zertifikatsverifizierung!
response = requests.get(url, verify=False)
# VERWUNDBAR: Socket ohne Verifizierung
import socket
import ssl
def connect_vulnerable(host, port):
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((host, port))
# Keine Zertifikatsverifizierung
context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
context.check_hostname = False
context.verify_mode = ssl.CERT_NONE
ssl_sock = context.wrap_socket(sock)
return ssl_sock
// VERWUNDBAR: Java TrustManager, der alle Zertifikate akzeptiert
public class VulnerableTrustManager implements X509TrustManager {
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType) {
// Tut nichts - akzeptiert alle Client-Zertifikate
}
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType) {
// Tut nichts - akzeptiert alle Server-Zertifikate!
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
}
// VERWUNDBAR: Verwendung des unsicheren Trust-Managers
public class VulnerableHttpClient {
public static HttpsURLConnection createConnection(String urlStr)
throws Exception {
TrustManager[] trustAll = new TrustManager[] {
new VulnerableTrustManager()
};
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, trustAll, new SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(
sslContext.getSocketFactory()
);
// Ebenfalls verwundbar: Hostname-Verifizierung deaktivieren
HttpsURLConnection.setDefaultHostnameVerifier(
(hostname, session) -> true // Akzeptiert jeden Hostname!
);
URL url = new URL(urlStr);
return (HttpsURLConnection) url.openConnection();
}
}
Lösungscode
// SICHER: Ordnungsgemäße Zertifikatsverifizierung
SSL_CTX* create_context_safe() {
SSL_CTX* ctx = SSL_CTX_new(TLS_client_method());
if (ctx == NULL) {
return NULL;
}
// Zertifikatsverifizierung aktivieren
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);
// Vertrauenswürdige CA-Zertifikate laden
if (!SSL_CTX_load_verify_locations(ctx, "/etc/ssl/certs/ca-certificates.crt", NULL)) {
// Oder Standard-CA-Store verwenden
if (!SSL_CTX_set_default_verify_paths(ctx)) {
SSL_CTX_free(ctx);
return NULL;
}
}
// Minimale TLS-Version setzen
SSL_CTX_set_min_proto_version(ctx, TLS1_2_VERSION);
return ctx;
}
// SICHER: Mit Hostname-Verifizierung verbinden
int connect_safe(SSL_CTX* ctx, const char* host, int port) {
SSL* ssl = SSL_new(ctx);
if (ssl == NULL) {
return -1;
}
// Erwarteten Hostname für Verifizierung setzen
if (!SSL_set1_host(ssl, host)) {
SSL_free(ssl);
return -1;
}
// Hostname-Prüfung aktivieren
SSL_set_hostflags(ssl, X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS);
int fd = create_socket(host, port);
if (fd < 0) {
SSL_free(ssl);
return -1;
}
SSL_set_fd(ssl, fd);
// TLS-Handshake durchführen
if (SSL_connect(ssl) <= 0) {
handle_ssl_error(ssl);
SSL_free(ssl);
close(fd);
return -1;
}
// Zertifikat verifizieren
long verify_result = SSL_get_verify_result(ssl);
if (verify_result != X509_V_OK) {
fprintf(stderr, "Zertifikatsverifizierung fehlgeschlagen: %s\n",
X509_verify_cert_error_string(verify_result));
SSL_shutdown(ssl);
SSL_free(ssl);
close(fd);
return -1;
}
// Hostname verifizieren (redundant wenn SSL_set1_host funktioniert hat)
X509* cert = SSL_get_peer_certificate(ssl);
if (cert == NULL) {
fprintf(stderr, "Kein Peer-Zertifikat\n");
SSL_shutdown(ssl);
SSL_free(ssl);
close(fd);
return -1;
}
X509_free(cert);
return fd; // File-Descriptor zurückgeben, ssl wird für I/O verwendet
}
// SICHER: Vollständige Verifizierung mit OCSP-Prüfung
SSL_CTX* 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);
// OCSP-Stapling aktivieren
SSL_CTX_set_tlsext_status_type(ctx, TLSEXT_STATUSTYPE_ocsp);
// OCSP-Callback setzen
SSL_CTX_set_tlsext_status_cb(ctx, ocsp_callback);
return ctx;
}
int ocsp_callback(SSL* ssl, void* arg) {
const unsigned char* ocsp_response;
int len = SSL_get_tlsext_status_ocsp_resp(ssl, &ocsp_response);
if (len <= 0) {
// Keine OCSP-Antwort - Richtlinie entscheiden
return 1; // Oder 0 um OCSP zu erfordern
}
// OCSP-Antwort verifizieren
// ... Verifizierungscode ...
return 1; // Erfolg
}
# SICHER: Python mit ordnungsgemäßer Zertifikatsverifizierung
import ssl
import socket
import certifi
# SICHER: Standard-Kontext mit Verifizierung verwenden
context = ssl.create_default_context()
# Verifizierung ist standardmäßig aktiviert
# System- oder certifi-CA-Bundle verwenden
context.load_verify_locations(certifi.where())
# Mit Hostname-Verifizierung verbinden
with socket.create_connection((host, port)) as sock:
with context.wrap_socket(sock, server_hostname=host) as ssock:
# Zertifikat und Hostname verifiziert
ssock.send(data)
# SICHER: Requests mit Verifizierung (Standard)
import requests
# Zertifikatsverifizierung ist standardmäßig aktiviert
response = requests.get(url) # verify=True ist Standard
# Oder explizit CA-Bundle angeben
response = requests.get(url, verify='/path/to/ca-bundle.crt')
# SICHER: Vollständige SSL-Kontext-Einrichtung
def create_secure_context():
context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
# Zertifikatsverifizierung aktivieren
context.verify_mode = ssl.CERT_REQUIRED
# Hostname-Prüfung aktivieren
context.check_hostname = True
# Vertrauenswürdige CAs laden
context.load_verify_locations(certifi.where())
# Minimale TLS-Version setzen
context.minimum_version = ssl.TLSVersion.TLSv1_2
return context
// SICHER: Java mit ordnungsgemäßer Zertifikatsvalidierung
public class SafeHttpClient {
public static HttpsURLConnection createSecureConnection(String urlStr)
throws Exception {
// Standard-SSLContext verwenden, der Zertifikate validiert
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, null, new SecureRandom());
URL url = new URL(urlStr);
HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
conn.setSSLSocketFactory(sslContext.getSocketFactory());
// Standard-Hostname-Verifier prüft Zertifikat CN/SAN
// conn.setHostnameVerifier() nicht aufgerufen - verwendet sicheren Standard
return conn;
}
// SICHER: Benutzerdefinierter Trust-Manager mit ordnungsgemäßer Validierung
public static SSLContext createCustomContext(String trustStorePath,
String password)
throws Exception {
// Trust-Store laden
KeyStore trustStore = KeyStore.getInstance("JKS");
try (FileInputStream fis = new FileInputStream(trustStorePath)) {
trustStore.load(fis, password.toCharArray());
}
// Trust-Manager erstellen
TrustManagerFactory tmf = TrustManagerFactory.getInstance(
TrustManagerFactory.getDefaultAlgorithm()
);
tmf.init(trustStore);
// SSL-Kontext initialisieren
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, tmf.getTrustManagers(), new SecureRandom());
return sslContext;
}
}
// SICHER: OkHttp mit Certificate-Pinning verwenden
import okhttp3.CertificatePinner;
import okhttp3.OkHttpClient;
public class SafeOkHttpClient {
public static OkHttpClient createPinnedClient() {
CertificatePinner pinner = new CertificatePinner.Builder()
.add("example.com", "sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=")
.build();
return new OkHttpClient.Builder()
.certificatePinner(pinner)
.build();
}
}
Ausgenutzt in der Praxis
Mobile App MITM-Angriffe
Mobile Anwendungen mit deaktivierter Zertifikatsvalidierung wurden in öffentlichen WLAN-Netzwerken ausgenutzt.
API-Credential-Diebstahl
Anwendungen, die Zertifikate nicht validierten, leakten API-Schlüssel an MITM-Angreifer.
Unternehmens-Datenpannen
Interne Tools mit deaktivierter Zertifikatsverifizierung exponierten sensible Unternehmensdaten.
Tools zum Testen/Ausnutzen
- mitmproxy — HTTPS-Traffic abfangen
- Burp Suite — Web-Sicherheitstests
- SSLstrip — SSL/TLS-Downgrade-Angriffe
- testssl.sh — SSL/TLS-Tests
CVE-Beispiele
- CVEs in Anwendungen, die ungültige Zertifikate akzeptieren
- Mobile-App-Zertifikatsvalidierungs-Bypasses
- Bibliotheks-Schwachstellen, die MITM-Angriffe ermöglichen
Referenzen
- MITRE. "CWE-599: Missing Validation of OpenSSL Certificate." https://cwe.mitre.org/data/definitions/599.html
- OpenSSL Documentation. SSL_CTX_set_verify, SSL_get_verify_result.