+
+
+ Using a RemoteCertificateValidationCallback
that always returns true
is insecure because it allows any certificate to be accepted as valid. This can lead to a variety of security issues, including man-in-the-middle attacks.
+
+
+
+
+ Ensure that RemoteCertificateValidationCallback
implementations properly verify the certificate before returning true
. Avoid implementing callbacks that unconditionally accept all certificates.
+
+
+
+
+ The following example demonstrates an insecure use of RemoteCertificateValidationCallback
that always returns true
:
+
+
+
+ A secure approach would involve proper verification of the certificate before returning true
:
+
+
+
+
+
+ CA5359: Do not disable certificate validation
+ CA5359
+
+
+
\ No newline at end of file
diff --git a/csharp/ql/src/experimental/CWE-295/InsecureCertificateValidation.ql b/csharp/ql/src/experimental/CWE-295/InsecureCertificateValidation.ql
new file mode 100644
index 000000000000..27258b56fd52
--- /dev/null
+++ b/csharp/ql/src/experimental/CWE-295/InsecureCertificateValidation.ql
@@ -0,0 +1,22 @@
+/**
+ * @name Unsafe `CertificateValidationCallback` use.
+ * @description Using a `CertificateValidationCallback` that always returns `true` is insecure, as it allows any certificate to be accepted as valid.
+ * @kind path-problem
+ * @problem.severity error
+ * @precision high
+ * @id cs/unsafe-certificate-validation
+ * @tags security
+ * external/cwe/cwe-295
+ * external/cwe/cwe-297
+ */
+
+import csharp
+import DataFlow
+import InsecureCertificateValidationQuery
+import InsecureCertificateValidation::PathGraph
+
+from InsecureCertificateValidation::PathNode source, InsecureCertificateValidation::PathNode sink
+where InsecureCertificateValidation::flowPath(source, sink)
+select sink.getNode(), source, sink,
+ "$@ that is defined $@ and accepts any certificate as valid, is used here.", sink,
+ "This certificate callback", source, "here"
diff --git a/csharp/ql/src/experimental/CWE-295/InsecureCertificateValidationExample.cs b/csharp/ql/src/experimental/CWE-295/InsecureCertificateValidationExample.cs
new file mode 100644
index 000000000000..76cddcce2915
--- /dev/null
+++ b/csharp/ql/src/experimental/CWE-295/InsecureCertificateValidationExample.cs
@@ -0,0 +1,2 @@
+ServicePointManager.ServerCertificateValidationCallback =
+ (sender, cert, chain, sslPolicyErrors) => true;
\ No newline at end of file
diff --git a/csharp/ql/src/experimental/CWE-295/InsecureCertificateValidationQuery.qll b/csharp/ql/src/experimental/CWE-295/InsecureCertificateValidationQuery.qll
new file mode 100644
index 000000000000..08f27cfa3ec0
--- /dev/null
+++ b/csharp/ql/src/experimental/CWE-295/InsecureCertificateValidationQuery.qll
@@ -0,0 +1,152 @@
+/**
+ * Provides a taint-tracking configuration for reasoning
+ * about insecure certificate validation vulnerabilities.
+ */
+
+private import csharp
+private import DataFlow
+private import semmle.code.csharp.controlflow.Guards
+
+/**
+ * A source specific to unsafe certificate validation vulnerabilities.
+ */
+abstract private class Source extends DataFlow::Node { }
+
+/**
+ * A sink specific to unsafe certificate validation vulnerabilities.
+ */
+abstract private class Sink extends DataFlow::Node { }
+
+/**
+ * A sanitizer specific to unsafe certificate validation vulnerabilities.
+ */
+abstract private class Sanitizer extends DataFlow::ExprNode { }
+
+/**
+ * Provides an abstract base class for properties related to certificate validation.
+ */
+abstract private class CertificateValidationProperty extends Property { }
+
+/**
+ * Represents the `ServerCertificateValidationCallback` property of the `ServicePointManager` class.
+ */
+private class ServicePointManagerServerCertificateValidationCallback extends CertificateValidationProperty
+{
+ ServicePointManagerServerCertificateValidationCallback() {
+ this.getDeclaringType().hasFullyQualifiedName("System.Net", "ServicePointManager") and
+ this.hasName("ServerCertificateValidationCallback")
+ }
+}
+
+/**
+ * Represents the `ServerCertificateCustomValidationCallback` property of the `HttpClientHandler` class.
+ */
+private class HttpClientHandlerServerCertificateCustomValidationCallback extends CertificateValidationProperty
+{
+ HttpClientHandlerServerCertificateCustomValidationCallback() {
+ this.getDeclaringType().hasFullyQualifiedName("System.Net.Http", "HttpClientHandler") and
+ this.hasName("ServerCertificateCustomValidationCallback")
+ }
+}
+
+/**
+ * Represents the `ServerCertificateValidationCallback` property of the `HttpWebRequest` class.
+ */
+private class HttpWebRequestServerCertificateValidationCallback extends CertificateValidationProperty
+{
+ HttpWebRequestServerCertificateValidationCallback() {
+ this.getDeclaringType().hasFullyQualifiedName("System.Net", "HttpWebRequest") and
+ this.hasName("ServerCertificateValidationCallback")
+ }
+}
+
+/**
+ * Represents the creation of an `SslStream` object.
+ */
+private class SslStreamCreation extends ObjectCreation {
+ SslStreamCreation() {
+ this.getObjectType().hasFullyQualifiedName("System.Net.Security", "SslStream")
+ }
+
+ /**
+ * Gets the expression used as the server certificate validation callback argument
+ * in the creation of the `SslStream` object.
+ */
+ Expr getServerCertificateValidationCallback() { result = this.getArgument(2) }
+}
+
+/**
+ * Holds if `c` always returns `true`.
+ */
+private predicate alwaysReturnsTrue(Callable c) {
+ forex(ReturnStmt rs | rs.getEnclosingCallable() = c |
+ rs.getExpr().(BoolLiteral).getBoolValue() = true
+ )
+ or
+ c.getBody().(BoolLiteral).getBoolValue() = true
+}
+
+/**
+ * Gets the actual callable object referred to by expression `e`.
+ * This can be a direct reference to a callable, a delegate creation, or an anonymous function.
+ */
+private Callable getActualCallable(Expr e) {
+ exists(Expr dcArg | dcArg = e.(DelegateCreation).getArgument() |
+ result = dcArg.(CallableAccess).getTarget() or
+ result = dcArg.(AnonymousFunctionExpr)
+ )
+ or
+ result = e
+}
+
+private predicate ignoreCertificateValidation(Guard guard, AbstractValue v) {
+ guard =
+ any(PropertyAccess access |
+ access.getProperty().hasFullyQualifiedName("", "Settings", "IgnoreCertificateValidation") and
+ v.(AbstractValues::BooleanValue).getValue() = true
+ )
+}
+
+private class AddIgnoreCheck extends Sanitizer {
+ AddIgnoreCheck() {
+ exists(Guard g, AbstractValues::BooleanValue v | ignoreCertificateValidation(g, v) |
+ g.controlsNode(this.getControlFlowNode(), v)
+ )
+ }
+}
+
+private class CallableAlwaysReturnsTrue extends Callable {
+ CallableAlwaysReturnsTrue() { alwaysReturnsTrue(this) }
+}
+
+private class AddCallableAlwaysReturnsTrue extends Source {
+ AddCallableAlwaysReturnsTrue() {
+ getActualCallable(this.asExpr()) instanceof CallableAlwaysReturnsTrue
+ }
+}
+
+private class AddSinks extends Sink {
+ AddSinks() {
+ exists(Expr e | e = this.asExpr() |
+ e =
+ [
+ any(CertificateValidationProperty p).getAnAssignedValue(),
+ any(SslStreamCreation yy).getServerCertificateValidationCallback()
+ ] and
+ not e.getFile().getAbsolutePath().matches("example")
+ )
+ }
+}
+
+private module InsecureCertificateValidationConfig implements DataFlow::ConfigSig {
+ predicate isSource(DataFlow::Node node) { node instanceof Source }
+
+ predicate isBarrier(DataFlow::Node node) { node instanceof Sanitizer }
+
+ predicate isSink(DataFlow::Node node) { node instanceof Sink }
+}
+
+/**
+ * A taint-tracking module for insecure certificate validation vulnerabilities.
+ */
+module InsecureCertificateValidation = TaintTracking::Global