Skip to content

Java: Promote Insecure TrustManager from experimental #7136

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,28 +1,52 @@
/** Provides classes and predicates to reason about insecure `TrustManager`s. */

import java
private import semmle.code.java.controlflow.Guards
private import semmle.code.java.security.Encryption
private import semmle.code.java.security.SecurityFlag

/** The creation of an insecure `TrustManager`. */
abstract class InsecureTrustManagerSource extends DataFlow::Node { }

private class DefaultInsecureTrustManagerSource extends InsecureTrustManagerSource {
DefaultInsecureTrustManagerSource() {
this.asExpr().(ClassInstanceExpr).getConstructedType() instanceof InsecureX509TrustManager
}
}

/**
* @name `TrustManager` that accepts all certificates
* @description Trusting all certificates allows an attacker to perform a machine-in-the-middle attack.
* @kind path-problem
* @problem.severity error
* @precision high
* @id java/insecure-trustmanager
* @tags security
* external/cwe/cwe-295
* The use of a `TrustManager` in an SSL context.
* Intentionally insecure connections are not considered sinks.
*/
abstract class InsecureTrustManagerSink extends DataFlow::Node {
InsecureTrustManagerSink() { not isGuardedByInsecureFlag(this) }
}

import java
import semmle.code.java.controlflow.Guards
import semmle.code.java.dataflow.DataFlow
import semmle.code.java.dataflow.FlowSources
import semmle.code.java.security.Encryption
import semmle.code.java.security.SecurityFlag
import DataFlow::PathGraph
private class DefaultInsecureTrustManagerSink extends InsecureTrustManagerSink {
DefaultInsecureTrustManagerSink() {
exists(MethodAccess ma, Method m |
m.hasName("init") and
m.getDeclaringType() instanceof SSLContext and
ma.getMethod() = m
|
ma.getArgument(1) = this.asExpr()
)
}
}

/** Holds if `node` is guarded by a flag that suggests an intentionally insecure use. */
private predicate isGuardedByInsecureFlag(DataFlow::Node node) {
exists(Guard g | g.controls(node.asExpr().getBasicBlock(), _) |
g = getASecurityFeatureFlagGuard() or g = getAnInsecureTrustManagerFlagGuard()
)
}

/**
* An insecure `X509TrustManager`.
* An `X509TrustManager` is considered insecure if it never throws a `CertificateException`
* and therefore implicitly trusts any certificate as valid.
*/
class InsecureX509TrustManager extends RefType {
private class InsecureX509TrustManager extends RefType {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Commenting here, because I can not comment down there, because the code did not change.
When I coded this, there was no good place for CertificateException (Line 37/61), maybe there is now a better place or common library?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the moment, this is something that is only used here, and it doesn't seem to fit in any of the currently existing libraries. Nonetheless, this is definitely something to keep in mind if, in the future, CertificateException needs to be reused, or more classes from java.security.cert are modeled. Thanks!

InsecureX509TrustManager() {
this.getASupertype*() instanceof X509TrustManager and
exists(Method m |
Expand Down Expand Up @@ -59,27 +83,6 @@ private predicate mayThrowCertificateException(Method m) {
)
}

/**
* A configuration to model the flow of an `InsecureX509TrustManager` to an `SSLContext.init` call.
*/
class InsecureTrustManagerConfiguration extends TaintTracking::Configuration {
InsecureTrustManagerConfiguration() { this = "InsecureTrustManagerConfiguration" }

override predicate isSource(DataFlow::Node source) {
source.asExpr().(ClassInstanceExpr).getConstructedType() instanceof InsecureX509TrustManager
}

override predicate isSink(DataFlow::Node sink) {
exists(MethodAccess ma, Method m |
m.hasName("init") and
m.getDeclaringType() instanceof SSLContext and
ma.getMethod() = m
|
ma.getArgument(1) = sink.asExpr()
)
}
}

/**
* Flags suggesting a deliberately insecure `TrustManager` usage.
*/
Expand All @@ -98,20 +101,3 @@ private class InsecureTrustManagerFlag extends FlagKind {
private Guard getAnInsecureTrustManagerFlagGuard() {
result = any(InsecureTrustManagerFlag flag).getAFlag().asExpr()
}

/** Holds if `node` is guarded by a flag that suggests an intentionally insecure use. */
private predicate isNodeGuardedByFlag(DataFlow::Node node) {
exists(Guard g | g.controls(node.asExpr().getBasicBlock(), _) |
g = getASecurityFeatureFlagGuard() or g = getAnInsecureTrustManagerFlagGuard()
)
}

from
DataFlow::PathNode source, DataFlow::PathNode sink, InsecureTrustManagerConfiguration cfg,
RefType trustManager
where
cfg.hasFlowPath(source, sink) and
not isNodeGuardedByFlag(sink.getNode()) and
trustManager = source.getNode().asExpr().(ClassInstanceExpr).getConstructedType()
select sink, source, sink, "$@ that is defined $@ and trusts any certificate, is used here.",
source, "This trustmanager", trustManager, "here"
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/** Provides taint tracking configurations to be used in Trust Manager queries. */

import java
import semmle.code.java.dataflow.FlowSources
import semmle.code.java.security.InsecureTrustManager

/**
* A configuration to model the flow of an insecure `TrustManager`
* to the initialization of an SSL context.
*/
class InsecureTrustManagerConfiguration extends DataFlow::Configuration {
InsecureTrustManagerConfiguration() { this = "InsecureTrustManagerConfiguration" }

override predicate isSource(DataFlow::Node source) {
source instanceof InsecureTrustManagerSource
}

override predicate isSink(DataFlow::Node sink) { sink instanceof InsecureTrustManagerSink }

override predicate allowImplicitRead(DataFlow::Node node, DataFlow::Content c) {
(this.isSink(node) or this.isAdditionalFlowStep(node, _)) and
node.getType() instanceof Array and
c instanceof DataFlow::ArrayContent
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
<qhelp>
<overview>
<p>
If the <code>checkServerTrusted</code> method of a <code>TrustManager</code> never throws a <code>CertificateException</code> it trusts every certificate.
This allows an attacker to perform a machine-in-the-middle attack against the application therefore breaking any security Transport Layer Security (TLS) gives.
If the <code>checkServerTrusted</code> method of a <code>TrustManager</code> never throws a <code>CertificateException</code>, it trusts every certificate.
This allows an attacker to perform a machine-in-the-middle attack against the application, therefore breaking any security Transport Layer Security (TLS) gives.
</p>

<p>
Expand Down Expand Up @@ -42,6 +42,6 @@ is loaded into a <code>KeyStore</code>. This explicitly defines the certificate
</example>

<references>
<li>Android Develoers:<a href="https://developer.android.com/training/articles/security-ssl">Security with HTTPS and SSL</a>.</li>
<li>Android Developers: <a href="https://developer.android.com/training/articles/security-ssl">Security with HTTPS and SSL</a>.</li>
</references>
</qhelp>
21 changes: 21 additions & 0 deletions java/ql/src/Security/CWE/CWE-295/InsecureTrustManager.ql
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/**
* @name `TrustManager` that accepts all certificates
* @description Trusting all certificates allows an attacker to perform a machine-in-the-middle attack.
* @kind path-problem
* @problem.severity error
* @security-severity 7.5
* @precision high
* @id java/insecure-trustmanager
* @tags security
* external/cwe/cwe-295
*/

import java
import semmle.code.java.dataflow.DataFlow
import semmle.code.java.security.InsecureTrustManagerQuery
import DataFlow::PathGraph

from DataFlow::PathNode source, DataFlow::PathNode sink
where any(InsecureTrustManagerConfiguration cfg).hasFlowPath(source, sink)
select sink, source, sink, "This $@, which is defined $@ and trusts any certificate, is used here.",
source, "TrustManager", source.getNode().asExpr().(ClassInstanceExpr).getConstructedType(), "here"
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---
category: newQuery
---
* The query "`TrustManager` that accepts all certificates" (`java/insecure-trustmanager`) has been promoted from experimental to the main query pack. Its results will now appear by default. This query was originally [submitted as an experimental query by @intrigus-lgtm](https://github.com/github/codeql/pull/4879).
Loading