Insecure SSL

ID

csharp.insecure_ssl

Severity

critical

Resource

Misconfiguration

Language

CSharp

Tags

CWE:295, NIST.SP.800-53, OWASP:2021:A2, OWASP:2021:A7, PCI-DSS:6.5.4

Description

Insecure SSL refers to the usage of insecure configurations with SSL/TLS protocols that could compromise data confidentiality, integrity, or authenticity while in transit.

Developers might inadvertently create SSL/TLS connections without enforcing adequate security measures, risking man-in-the-middle (MITM) attacks.

Rationale

The danger of insecure SSL configurations generally stem from mistakes such as using outdated SSL/TLS protocols, disabling certificate validation, or failing to verify hostnames. Code snippets that disable these validations are useful for quick testing. However, leaving them in production can create serious security vulnerabilities.

SSL/TLS security is undermined by:

  1. Trusting all certificates, effectively ignoring trust chain verification.

  2. Allowing all hostnames to be valid, circumventing checks for URL domain validity and authenticity.

  3. Disabling Certificate Revocation List (CRL) or Online Certificate Status Protocol (OCSP) checks for certificate revocation.

  4. Using insecure protocols like SSLv3 and TLS 1.0 / 1.1.

The modern HttpClient framework can be configured via System.Net.Http.HttpClientHandler.

using System;
using System.Net.Http;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
using static System.Net.Http.HttpClientHandler;

// ...

HttpClientHandler handler = new HttpClientHandler
{
    // FLAW: Set up an insecure certificate validation callback
    ServerCertificateCustomValidationCallback =
        (sender, certificate, chain, sslPolicyErrors) => true,
    // or set the accept-any delegate:
    ServerCertificateCustomValidationCallback =
        DangerousAcceptAnyServerCertificateValidator,

    // FLAW: Bypass CRL (Certificate Revocation List) checking
    CheckCertificateRevocationList = false,

    // FLAW: SSLv3 and TLS 1.0/1.1 are deemed insecure
    SslOptions.EnabledSslProtocols =
        SslProtocols.Ssl3 | SslProtocols.Tls
        | SslProtocols.Tls11 | SslProtocols.Tls12
        | SslProtocols.Tls13
};

// Connect to endpoint allowing insecure SSL
using (var client = new HttpClient(handler))
{
    client.BaseAddress = new Uri("https://example.com");
    // Use the client...
}

For the (legacy) WebRequest / WebClient framework, System.Net.ServicePointManager is used to configure SSL/TLS security settings. Here’s a minimal example that highlights typical insecure coding practices with SSL:

using System;
using System.Net;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
// ...

// VULNERABLE: Set up an insecure certificate validation callback
ServicePointManager.ServerCertificateValidationCallback =
    (sender, certificate, chain, sslPolicyErrors) => true;

// VULNERABLE: Disable CRL checking
ServicePointManager.CheckCertificateRevocationList = false;

// VULNERABLE: SSLv3 and TLS 1.0/1.1 are deemed insecure
ServicePointManager.SecurityProtocol =
    SecurityProtocolType.Ssl3
    | SecurityProtocolType.Tls
    | SecurityProtocolType.Tls11
    | SecurityProtocolType.Tls12
    | SecurityProtocolType.Tls13;

// Connect to endpoint allowing insecure SSL
using (var client = new WebClient())
{
   // ... use client to connect to endpoint
}

For low level access to SSL/TLS connections, the System.Net.Security.SslStream class can be configured.

using System;
using System.IO;
using System.Net.Security;
using System.Net.Sockets;
using System.Security.Authentication;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading.Tasks;

public class UnsafeSslStreamExample
{
    public static async Task Main(string[] args)
    {
        await ConnectWithUnsafeSslStream("example.com", 443);
    }

    public static async Task ConnectWithUnsafeSslStream(string hostname, int port)
    {
        // Create TCP connection
        using var tcpClient = new TcpClient();
        await tcpClient.ConnectAsync(hostname, port);

        // VULNERABLE - Create SslStream with unsafe certificate validation
        using var sslStream = new SslStream(
            tcpClient.GetStream(),
            false, // leaveInnerStreamOpen
            ValidateServerCertificate // Certificate validation callback - accepts everything
        );

        try
        {
            // VULNERABLE - unsafe SSL/TLS protocols and no CRL checking
            await sslStream.AuthenticateAsClientAsync(
                hostname,
                null, // clientCertificates not used
                SslProtocols.Default, // Include deprecated protocols
                false // checkCertificateRevocation - disabled CRL checking
            );

            // Fire a simple HTTP request
            string httpRequest = $"GET / HTTP/1.1\r\nHost: {hostname}\r\nConnection: close\r\n\r\n";
            byte[] requestBytes = Encoding.UTF8.GetBytes(httpRequest);
            await sslStream.WriteAsync(requestBytes, 0, requestBytes.Length);
            // ... read response
        }
        catch (AuthenticationException ex)
        {
            Console.WriteLine($"Authentication failed: {ex.Message}");
        }
    }

    // Unsafe certificate validation - accepts ANY certificate and ANY hostname
    private static bool ValidateServerCertificate(
        object sender, X509Certificate certificate,
        X509Chain chain, SslPolicyErrors sslPolicyErrors)
    {
        // VULNERABLE: Always return true
        // This accepts:
        // - Any certificate (valid, invalid, expired, self-signed)
        // - Any hostname (bypasses hostname verification)
        // - Any certificate chain errors
        return true;
    }
}

Another unsafe configuration for the ASP.NET Core Kestrel web server follows:

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Server.Kestrel.Core;
using Microsoft.AspNetCore.Server.Kestrel.Https;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;
using System.Net;
using System.Net.Security;
using System.Security.Authentication;
using System.Security.Cryptography.X509Certificates;

public static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
        .ConfigureWebHostDefaults(webBuilder =>
        {
            webBuilder.UseStartup<Startup>();
            webBuilder.ConfigureKestrel(ConfigureUnsafeKestrel);
        });

private static void ConfigureUnsafeKestrel(KestrelServerOptions options)
{
    // Configure HTTP endpoint
    options.Listen(IPAddress.Any, 8080);

    // Configure HTTPS endpoint with maximally unsafe SSL/TLS settings
    options.Listen(IPAddress.Any, 8443, listenOptions =>
    {
        listenOptions.UseHttps(httpsOptions =>
        {
            // Use a self-signed certificate or any certificate
            httpsOptions.ServerCertificate = GenerateSelfSignedCertificate();

            // VULNERABLE: unsafe SSL protocols, including SSLv3 and TLS 1.0/1.1
            httpsOptions.SslProtocols = SslProtocols.Default;

            // VULNERABLE: Disable certificate revocation checking
            httpsOptions.CheckCertificateRevocation = false;

            // enable client certificate validation
            httpsOptions.ClientCertificateMode =
                ClientCertificateMode.AllowCertificate;

            // VULNERABLE: accept *any* client certificate
            httpsOptions.ClientCertificateValidation = (certificate, chain, policyErrors) => true;
            // alternative to accept *any* client certificate
            // httpsOptions.AllowAnyClientCertificate();
        });
    });
}

These weak configurations undermine security, making it easy for an attacker to intercept or alter communications without being detected.

Remediation

Fixing the insecure HttpClientHandler configuration goes like this:

// Leave system defaults for SSL/TLS (secure by default in .NET Core 5+)
var handler = new HttpClientHandler()
{
    // FIX: Enforce certificate validation
    ServerCertificateCustomValidationCallback = (sender, cert, chain, sslPolicyErrors) =>
        sslPolicyErrors == SslPolicyErrors.None,

    // FIX: Enable certificate revocation checks
    CheckCertificateRevocationList = true
};

For the ServicePointManager example, the default configuration is generally secure. If you need to weaken it for testing purposes, ensure that the build for production never uses the weakened configuration. The safe default is like this:

ServicePointManager.ServerCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) => sslPolicyErrors == SslPolicyErrors.None;

ServicePointManager.CheckCertificateRevocationList = false;

// No need to weaken the default SecurityProtocols allowed;
// but if needed you can force TLS 1.2 or 1.3 only
// (or SecurityProtocolType.SystemDefault)
ServicePointManager.SecurityProtocol =
    SecurityProtocolType.Tls12
#if NET5_0_OR_GREATER
    | SecurityProtocolType.Tls13
#endif
    ;

And finally, for the low-level SslStream example, fix the insecure configuration like this:

    public static async Task ConnectWithSecureSslStream(string hostname, int port)
    {
        using var tcpClient = new TcpClient();
        await tcpClient.ConnectAsync(hostname, port);

        // FIX: Secure SslStream with proper certificate validation
        using var sslStream = new SslStream(
            tcpClient.GetStream(),
            leaveInnerStreamOpen: false,
            ValidateServerCertificate // reject on any errors
        );

        try
        {
            // FIX: Secure SSL/TLS protocols and enable CRL checking
            await sslStream.AuthenticateAsClientAsync(
                targetHost: hostname,
                clientCertificates: null,
                enabledSslProtocols: SslProtocols.SystemDefault,
                checkCertificateRevocation: true
            );

            // Use the stream...
        }
        catch (AuthenticationException ex)
        {
            Console.WriteLine($"Authentication failed: {ex.Message}");
        }
    }

    // FIX: Secure certificate validation: reject on any errors
    private static bool ValidateServerCertificate(
        object sender,
        X509Certificate certificate,
        X509Chain chain,
        SslPolicyErrors sslPolicyErrors)
    {
        return sslPolicyErrors == SslPolicyErrors.None;
    }

To remediate issues related to insecure SSL configurations:

  1. Use a Trust Store: Always configure the software to use a well-maintained trust store with up-to-date certificates. Avoid using custom trust managers that disable certificate validation.

  2. Enforce Hostname Verification: Ensure that hostname verification is enabled so that the TLS clients can match the server’s hostname against its certificate’s naming information.

  3. Keep Protocols and Libraries Up-to-date: Regularly update the runtime environment and any third-party libraries to ensure compatibility with the latest secure protocols (e.g., TLS 1.2 or 1.3), avoiding outdated versions like SSLv3.

  4. Review Libraries for Vulnerabilities: Regularly audit the libraries and dependencies used in your software projects for known vulnerabilities.

References