Missing Validation of OpenSSL Certificate

Description

Missing Validation of OpenSSL Certificate occurs when an application using OpenSSL fails to properly verify SSL/TLS certificates during secure connections. This includes failing to check the certificate chain, not verifying the hostname matches the certificate's Common Name or Subject Alternative Names, accepting expired certificates, or not checking certificate revocation status. The application establishes connections without proper identity verification.

Risk

Without certificate validation, attackers can perform man-in-the-middle attacks using self-signed or improperly issued certificates. Connections believed to be secure actually provide no authentication of the remote server. Sensitive data (credentials, personal information, financial data) transmitted over these connections can be intercepted and read. The application may connect to malicious servers impersonating legitimate services.

Solution

Enable certificate verification with SSL_CTX_set_verify() and SSL_VERIFY_PEER. Load trusted CA certificates with SSL_CTX_load_verify_locations(). Verify hostname matches certificate using SSL_set1_host() or manual verification. Check SSL_get_verify_result() after connection. Consider checking certificate revocation status via OCSP or CRLs. Use modern TLS versions and disable insecure protocols.

Common Consequences

ImpactDetails
ConfidentialityScope: Data Interception

Attackers can intercept encrypted communications.
AuthenticationScope: Server Impersonation

Cannot verify connecting to intended server.
IntegrityScope: Data Tampering

MITM attackers can modify data in transit.

Example Code + Solution Code

Vulnerable Code

// VULNERABLE: No certificate verification
SSL_CTX* create_context_vulnerable() {
    SSL_CTX* ctx = SSL_CTX_new(TLS_client_method());

    // No verification set - accepts ANY certificate!
    // SSL_CTX_set_verify not called

    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);

    // Connects without verifying server identity!
    if (SSL_connect(ssl) <= 0) {
        handle_error();
        return;
    }

    // MITM attacker can present any certificate
    send_sensitive_data(ssl);
}

// VULNERABLE: Verification disabled
SSL_CTX* create_context_insecure() {
    SSL_CTX* ctx = SSL_CTX_new(TLS_client_method());

    // Explicitly disables verification!
    SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, NULL);

    return ctx;
}

// VULNERABLE: Verification enabled but no CA loaded
SSL_CTX* create_context_no_ca() {
    SSL_CTX* ctx = SSL_CTX_new(TLS_client_method());

    // Verification requested but will always fail
    // because no trusted CAs are loaded
    SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);

    // Missing: SSL_CTX_load_verify_locations()

    return ctx;
}

// VULNERABLE: No hostname verification
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) {
        // Certificate might be valid but for different host!
        // Attacker with any valid cert can intercept
        if (SSL_get_verify_result(ssl) == X509_V_OK) {
            // BUG: Didn't verify hostname matches certificate
            send_data(ssl);
        }
    }
}

// VULNERABLE: Ignoring verification result
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);  // May fail verification

    // BUG: Never checked SSL_get_verify_result()!
    send_sensitive_data(ssl);
}
# VULNERABLE: Python with disabled certificate verification
import ssl
import urllib.request

# VULNERABLE: Disable all certificate checks
context = ssl.create_default_context()
context.check_hostname = False
context.verify_mode = ssl.CERT_NONE

response = urllib.request.urlopen(url, context=context)

# VULNERABLE: Using deprecated/insecure methods
import requests

# Disables certificate verification!
response = requests.get(url, verify=False)

# VULNERABLE: Socket without verification
import socket
import ssl

def connect_vulnerable(host, port):
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.connect((host, port))

    # No certificate verification
    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
// VULNERABLE: Java TrustManager that accepts all certificates
public class VulnerableTrustManager implements X509TrustManager {

    @Override
    public void checkClientTrusted(X509Certificate[] chain, String authType) {
        // Does nothing - accepts all client certificates
    }

    @Override
    public void checkServerTrusted(X509Certificate[] chain, String authType) {
        // Does nothing - accepts all server certificates!
    }

    @Override
    public X509Certificate[] getAcceptedIssuers() {
        return new X509Certificate[0];
    }
}

// VULNERABLE: Using the insecure trust manager
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()
        );

        // Also vulnerable: disable hostname verification
        HttpsURLConnection.setDefaultHostnameVerifier(
            (hostname, session) -> true  // Accepts any hostname!
        );

        URL url = new URL(urlStr);
        return (HttpsURLConnection) url.openConnection();
    }
}

Fixed Code

// SAFE: Proper certificate verification
SSL_CTX* create_context_safe() {
    SSL_CTX* ctx = SSL_CTX_new(TLS_client_method());
    if (ctx == NULL) {
        return NULL;
    }

    // Enable certificate verification
    SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);

    // Load trusted CA certificates
    if (!SSL_CTX_load_verify_locations(ctx, "/etc/ssl/certs/ca-certificates.crt", NULL)) {
        // Or use default CA store
        if (!SSL_CTX_set_default_verify_paths(ctx)) {
            SSL_CTX_free(ctx);
            return NULL;
        }
    }

    // Set minimum TLS version
    SSL_CTX_set_min_proto_version(ctx, TLS1_2_VERSION);

    return ctx;
}

// SAFE: Connect with hostname verification
int connect_safe(SSL_CTX* ctx, const char* host, int port) {
    SSL* ssl = SSL_new(ctx);
    if (ssl == NULL) {
        return -1;
    }

    // Set expected hostname for verification
    if (!SSL_set1_host(ssl, host)) {
        SSL_free(ssl);
        return -1;
    }

    // Enable hostname checking
    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);

    // Perform TLS handshake
    if (SSL_connect(ssl) <= 0) {
        handle_ssl_error(ssl);
        SSL_free(ssl);
        close(fd);
        return -1;
    }

    // Verify certificate
    long verify_result = SSL_get_verify_result(ssl);
    if (verify_result != X509_V_OK) {
        fprintf(stderr, "Certificate verification failed: %s\n",
                X509_verify_cert_error_string(verify_result));
        SSL_shutdown(ssl);
        SSL_free(ssl);
        close(fd);
        return -1;
    }

    // Verify hostname (redundant if SSL_set1_host worked)
    X509* cert = SSL_get_peer_certificate(ssl);
    if (cert == NULL) {
        fprintf(stderr, "No peer certificate\n");
        SSL_shutdown(ssl);
        SSL_free(ssl);
        close(fd);
        return -1;
    }
    X509_free(cert);

    return fd;  // Return file descriptor, ssl is used for I/O
}

// SAFE: Complete verification with OCSP checking
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);

    // Enable OCSP stapling
    SSL_CTX_set_tlsext_status_type(ctx, TLSEXT_STATUSTYPE_ocsp);

    // Set OCSP callback
    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) {
        // No OCSP response - decide policy
        return 1;  // Or 0 to require OCSP
    }

    // Verify OCSP response
    // ... verification code ...

    return 1;  // Success
}
# SAFE: Python with proper certificate verification
import ssl
import socket
import certifi

# SAFE: Using default context with verification
context = ssl.create_default_context()
# Verification is enabled by default

# Use system or certifi CA bundle
context.load_verify_locations(certifi.where())

# Connect with hostname verification
with socket.create_connection((host, port)) as sock:
    with context.wrap_socket(sock, server_hostname=host) as ssock:
        # Certificate and hostname verified
        ssock.send(data)

# SAFE: Requests with verification (default)
import requests

# Certificate verification is on by default
response = requests.get(url)  # verify=True is default

# Or explicitly specify CA bundle
response = requests.get(url, verify='/path/to/ca-bundle.crt')

# SAFE: Complete SSL context setup
def create_secure_context():
    context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)

    # Enable certificate verification
    context.verify_mode = ssl.CERT_REQUIRED

    # Enable hostname checking
    context.check_hostname = True

    # Load trusted CAs
    context.load_verify_locations(certifi.where())

    # Set minimum TLS version
    context.minimum_version = ssl.TLSVersion.TLSv1_2

    return context
// SAFE: Java with proper certificate validation
public class SafeHttpClient {

    public static HttpsURLConnection createSecureConnection(String urlStr)
            throws Exception {
        // Use default SSLContext which validates certificates
        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());

        // Default hostname verifier checks certificate CN/SAN
        // conn.setHostnameVerifier() not called - uses secure default

        return conn;
    }

    // SAFE: Custom trust manager with proper validation
    public static SSLContext createCustomContext(String trustStorePath,
                                                  String password)
            throws Exception {
        // Load trust store
        KeyStore trustStore = KeyStore.getInstance("JKS");
        try (FileInputStream fis = new FileInputStream(trustStorePath)) {
            trustStore.load(fis, password.toCharArray());
        }

        // Create trust manager
        TrustManagerFactory tmf = TrustManagerFactory.getInstance(
            TrustManagerFactory.getDefaultAlgorithm()
        );
        tmf.init(trustStore);

        // Initialize SSL context
        SSLContext sslContext = SSLContext.getInstance("TLS");
        sslContext.init(null, tmf.getTrustManagers(), new SecureRandom());

        return sslContext;
    }
}

// SAFE: Using OkHttp with certificate pinning
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();
    }
}

Exploited in the Wild

Mobile App MITM Attacks

Mobile applications with disabled certificate validation were exploited on public WiFi networks.

API Credential Theft

Applications that didn't validate certificates leaked API keys to MITM attackers.

Enterprise Data Breaches

Internal tools with certificate verification disabled exposed sensitive corporate data.


Tools to test/exploit


CVE Examples

  • CVEs in applications accepting invalid certificates.

  • Mobile app certificate validation bypasses.

  • Library vulnerabilities allowing MITM attacks.


References

  1. MITRE. "CWE-599: Missing Validation of OpenSSL Certificate." https://cwe.mitre.org/data/definitions/599.html

  2. OpenSSL Documentation. SSL_CTX_set_verify, SSL_get_verify_result.