Don't disable certificate validation

Rule ND3103 Security

Matched methods replace the certificate validation of a TLS client or server: ServicePointManager.ServerCertificateValidationCallback, HttpClientHandler.ServerCertificateCustomValidationCallback (or its DangerousAcceptAnyServerCertificateValidator), SslClientAuthenticationOptions.RemoteCertificateValidationCallback (used by SocketsHttpHandler), an SslStream constructor taking a callback, the obsolete ServicePointManager.CertificatePolicy, or Kestrel's ClientCertificateValidation. These callbacks are mostly written to accept a self-signed or expired certificate, which means trusting any certificate: a vulnerability to man-in-the-middle attacks https://en.wikipedia.org/wiki/Man-in-the-middle_attack

This is CWE-295 Improper Certificate Validation.

Flagged Code
public HttpClient CreateClient() {
   var handler = new HttpClientHandler();
   // flagged: any certificate is trusted, a man-in-the-middle goes unnoticed
   handler.ServerCertificateCustomValidationCallback = (msg, cert, chain, errors) => true;
   return new HttpClient(handler);
}
Fixed Code
public HttpClient CreateClient() {
   // The issuing CA is installed in the machine trust store: the default
   // validation checks the whole chain, no callback is needed.
   var handler = new HttpClientHandler();
   return new HttpClient(handler);
}

How to fix

Install the issuing CA in the machine trust store, or pin the expected certificate thumbprint inside the callback instead of returning true. Never ship DangerousAcceptAnyServerCertificateValidator outside a development build. Suppress issues for legitimate custom validation procedures that still verify the chain.

CQLinq source

This rule is written in CQLinq, C# LINQ over the NDepend code model. It ships with every new NDepend project: you can read it, adapt it, or use it as a starting point for your own custom rules.

// <Name>Don't disable certificate validation</Name>
// <Id>ND3103:DontDisableCertificateValidation</Id>
warnif count > 0

// Third-party members are part of the code model only when the application uses them.
let certificateValidationOverrides = ThirdParty.Methods.Where(m =>
     m.ParentNamespace.Name.StartsWithAny("System.Net", "Microsoft.AspNetCore") &&
     (m.SimpleNameEqualsAny(
        "set_ServerCertificateValidationCallback",         // ServicePointManager, HttpWebRequest, WebRequestHandler
        "set_ServerCertificateCustomValidationCallback",   // HttpClientHandler
        "get_DangerousAcceptAnyServerCertificateValidator",// HttpClientHandler
        "set_RemoteCertificateValidationCallback",         // SslClientAuthenticationOptions, SslServerAuthenticationOptions (SocketsHttpHandler.SslOptions)
        "set_CertificatePolicy",                           // ServicePointManager (obsolete ICertificatePolicy)
        "set_ClientCertificateValidation") ||              // Kestrel HttpsConnectionAdapterOptions
      // new SslStream(stream, leaveOpen, RemoteCertificateValidationCallback)
      (m.IsConstructor && m.ParentType.FullName == "System.Net.Security.SslStream" && m.Name.Contains("RemoteCertificateValidationCallback"))))
   .ToHashSetEx()

where certificateValidationOverrides.Any()

from m in Application.Methods.UsingAny(certificateValidationOverrides)
let certificateValidationOverridesUsed = m.MethodsCalled.Intersect(certificateValidationOverrides).ToArray()
select new {
  m,
  certificateValidationOverridesUsed,
  Debt = 30.ToMinutes().ToDebt(),
  Severity = Severity.High
}

//<Expl>
// The *{0}* overrides certificate validation through *{1}*.
// A callback that returns true (or *DangerousAcceptAnyServerCertificateValidator*)
// makes the client trust any certificate, opening the door to *man-in-the-middle attacks*.
//</Expl>

//<Desc>
// Matched methods replace the certificate validation of a TLS client or
// server: *ServicePointManager.ServerCertificateValidationCallback*,
// *HttpClientHandler.ServerCertificateCustomValidationCallback* (or its
// *DangerousAcceptAnyServerCertificateValidator*),
// *SslClientAuthenticationOptions.RemoteCertificateValidationCallback*
// (used by *SocketsHttpHandler*), an *SslStream* constructor taking a
// callback, the obsolete *ServicePointManager.CertificatePolicy*, or
// Kestrel's *ClientCertificateValidation*. These callbacks are mostly
// written to accept a self-signed or expired certificate, which means
// trusting any certificate: a **vulnerability to man-in-the-middle
// attacks** https://en.wikipedia.org/wiki/Man-in-the-middle_attack
//
// This is CWE-295 *Improper Certificate Validation*.
//
// **Flagged Code**
// ```csharp
// public HttpClient CreateClient() {
//    var handler = new HttpClientHandler();
//    // flagged: any certificate is trusted, a man-in-the-middle goes unnoticed
//    handler.ServerCertificateCustomValidationCallback = (msg, cert, chain, errors) => true;
//    return new HttpClient(handler);
// }
// ```
//
// **Fixed Code**
// ```csharp
// public HttpClient CreateClient() {
//    // The issuing CA is installed in the machine trust store: the default
//    // validation checks the whole chain, no callback is needed.
//    var handler = new HttpClientHandler();
//    return new HttpClient(handler);
// }
// ```
//</Desc>

//<HowToFix>
// Install the issuing CA in the machine trust store, or pin the expected
// certificate thumbprint inside the callback instead of returning true.
// Never ship *DangerousAcceptAnyServerCertificateValidator* outside a
// development build. Suppress issues for legitimate custom validation
// procedures that still verify the chain.
//</HowToFix>

Security: all 39 items