Skip to content

Add CWE-295 query for C# (accepting any TLS certificate)#22019

Draft
owen-mc with Copilot wants to merge 3 commits into
mainfrom
copilot/add-cwe-295-query-for-csharp
Draft

Add CWE-295 query for C# (accepting any TLS certificate)#22019
owen-mc with Copilot wants to merge 3 commits into
mainfrom
copilot/add-cwe-295-query-for-csharp

Conversation

Copilot AI commented Jun 19, 2026

Copy link
Copy Markdown
Contributor

C# lacked a CWE-295 (Improper Certificate Validation) query that other languages already ship. This adds one detecting TLS/SSL certificate validation callbacks that trust every certificate, enabling machine-in-the-middle attacks.

Query

  • New cs/accept-any-certificate (Security Features/CWE-295/AcceptAnyCertificate.ql), a path-problem dataflow query.
  • Source: a callback that always returns true — a lambda/anonymous method, a method-group reference to a method that always returns true, or HttpClientHandler.DangerousAcceptAnyServerCertificateValidator.
  • Sink: a value used as a certificate validation callback — assigned to, or passed as an argument of, a delegate type returning bool with a System.Net.Security.SslPolicyErrors parameter. Covers RemoteCertificateValidationCallback (ServicePointManager, HttpWebRequest, SslStream) and HttpClientHandler.ServerCertificateCustomValidationCallback.
  • Acceptance is determined structurally: every returned value is the constant true, so callbacks that inspect SslPolicyErrors are not flagged.

Supporting files

  • .qhelp with good/bad samples, and a newQuery change note.
  • Query test under query-tests/Security Features/CWE-295/ covering inline lambdas, block bodies, method groups, SslStream, variable indirection, and the built-in dangerous validator, plus negative cases that perform real validation.
// Flagged: trusts any certificate
handler.ServerCertificateCustomValidationCallback =
    (request, certificate, chain, errors) => true;

// Not flagged: only trusts when validation succeeds
handler.ServerCertificateCustomValidationCallback =
    (request, certificate, chain, errors) => errors == SslPolicyErrors.None;

The query resolves into the default csharp-code-scanning suite via its metadata.

@owen-mc

owen-mc commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

@copilot Compare this pull request with #1643 and combine the best of both.

Copilot AI commented Jul 2, 2026

Copy link
Copy Markdown
Contributor Author

@copilot Compare this pull request with #1643 and combine the best of both.

Compared with #1643 and merged its strengths into this PR (commit 4d3d1cf):

  • Ignore dead-code returns: the "always returns true" check now filters with isLive(), so unreachable returns no longer suppress detection (and its control-flow negative case is handled).
  • Compound assignment (+=): added handling for the x += callback pattern used to combine delegates, which is the common real-world usage on ServicePointManager.ServerCertificateValidationCallback.
  • Test cases from C#: New query cs/certificate-validation-disabled #1643: += variants, parameterless delegate { return true; }, and static/instance method groups (incl. new RemoteCertificateValidationCallback(this.M)), plus a control-flow negative case.

This PR keeps its broader coverage that #1643 lacked: SslStream/HttpClientHandler.ServerCertificateCustomValidationCallback sinks, the built-in DangerousAcceptAnyServerCertificateValidator source, the modern DataFlow::Global API, and a .qhelp plus change note.

Copilot AI requested a review from owen-mc July 2, 2026 19:45
@owen-mc

owen-mc commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

@copilot Turn the test into an inline expectations test. The qlref file should be in this format:

query: X
postprocess: utils/test/InlineExpectationsTestQuery.ql

And the C# source file should have // $ Alert on any line with an alert, and // $ Source on any line with a source which isn't the same line as the alert.

@github-actions

github-actions Bot commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

QHelp previews:

csharp/ql/src/Security Features/CWE-295/AcceptAnyCertificate.qhelp

Accepting any TLS certificate during validation

A TLS/SSL certificate validation callback that always returns true trusts every certificate, regardless of any validation errors that were detected. This allows an attacker to perform a machine-in-the-middle attack against the application, therefore breaking any security that Transport Layer Security (TLS) provides.

An attack might look like this:

  1. The vulnerable program connects to https://example.com.
  2. The attacker intercepts this connection and presents a valid, self-signed certificate for https://example.com.
  3. The vulnerable program calls the certificate validation callback to check whether it should trust the certificate.
  4. The callback ignores the SslPolicyErrors argument and returns true.
  5. The vulnerable program accepts the certificate and proceeds with the connection, since the callback indicated that the certificate is trusted.
  6. The attacker can now read the data the program sends to https://example.com and/or alter its replies while the program thinks the connection is secure.

Recommendation

Do not use a certificate validation callback that unconditionally returns true. Either rely on the default certificate validation, or implement a callback that inspects the SslPolicyErrors argument and only trusts a specific, known certificate (for example, when using a self-signed certificate that has been explicitly pinned).

Example

In the first (bad) example, the callback always returns true and therefore trusts any certificate, which allows an attacker to perform a machine-in-the-middle attack. In the second (good) example, the callback returns true only when there are no validation errors.

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

public class CertificateValidation
{
    public void Bad()
    {
        var handler = new HttpClientHandler();
        // BAD: the callback always returns true, so every certificate is trusted.
        handler.ServerCertificateCustomValidationCallback =
            (request, certificate, chain, errors) => true;
    }

    public void Good()
    {
        var handler = new HttpClientHandler();
        // GOOD: the certificate is only trusted when there are no validation errors.
        handler.ServerCertificateCustomValidationCallback =
            (request, certificate, chain, errors) => errors == SslPolicyErrors.None;
    }
}

References

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants