-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #6171 from atorralba/atorralba/promote-unsafe-cert…
…ificate-trust Java: Promote Unsafe certificate trust query from experimental
- Loading branch information
Showing
20 changed files
with
791 additions
and
291 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
/** Provides classes and predicates to reason about unsafe certificate trust vulnerablities. */ | ||
|
||
import java | ||
private import semmle.code.java.frameworks.Networking | ||
private import semmle.code.java.security.Encryption | ||
private import semmle.code.java.dataflow.DataFlow | ||
|
||
/** | ||
* The creation of an object that prepares an SSL connection. | ||
* | ||
* This is a source for `SslEndpointIdentificationFlowConfig`. | ||
*/ | ||
class SslConnectionInit extends DataFlow::Node { | ||
SslConnectionInit() { | ||
exists(MethodAccess ma, Method m | | ||
this.asExpr() = ma and | ||
ma.getMethod() = m | ||
| | ||
m instanceof CreateSslEngineMethod | ||
or | ||
m instanceof CreateSocketMethod and isSslSocket(ma) | ||
) | ||
} | ||
} | ||
|
||
/** | ||
* A call to a method that establishes an SSL connection. | ||
* | ||
* This is a sink for `SslEndpointIdentificationFlowConfig`. | ||
*/ | ||
class SslConnectionCreation extends DataFlow::Node { | ||
SslConnectionCreation() { | ||
exists(MethodAccess ma, Method m | | ||
m instanceof BeginHandshakeMethod or | ||
m instanceof SslWrapMethod or | ||
m instanceof SslUnwrapMethod or | ||
m instanceof SocketGetOutputStreamMethod | ||
| | ||
ma.getMethod() = m and | ||
this.asExpr() = ma.getQualifier() | ||
) | ||
} | ||
} | ||
|
||
/** | ||
* An SSL object that correctly verifies hostnames, or doesn't need to (for instance, because it's a server). | ||
* | ||
* This is a sanitizer for `SslEndpointIdentificationFlowConfig`. | ||
*/ | ||
abstract class SslUnsafeCertTrustSanitizer extends DataFlow::Node { } | ||
|
||
/** | ||
* An `SSLEngine` set in server mode. | ||
*/ | ||
private class SslEngineServerMode extends SslUnsafeCertTrustSanitizer { | ||
SslEngineServerMode() { | ||
exists(MethodAccess ma, Method m | | ||
m.hasName("setUseClientMode") and | ||
m.getDeclaringType().getASupertype*() instanceof SSLEngine and | ||
ma.getMethod() = m and | ||
ma.getArgument(0).(CompileTimeConstantExpr).getBooleanValue() = false and | ||
this.asExpr() = ma.getQualifier() | ||
) | ||
} | ||
} | ||
|
||
/** | ||
* Holds if the return value of `createSocket` is cast to `SSLSocket` | ||
* or the qualifier of `createSocket` is an instance of `SSLSocketFactory`. | ||
*/ | ||
private predicate isSslSocket(MethodAccess createSocket) { | ||
createSocket = any(CastExpr ce | ce.getType() instanceof SSLSocket).getExpr() | ||
or | ||
createSocket.getQualifier().getType().(RefType).getASupertype*() instanceof SSLSocketFactory | ||
} | ||
|
||
/** | ||
* A call to a method that enables SSL (`useSslProtocol` or `setSslContextFactory`) | ||
* on an instance of `com.rabbitmq.client.ConnectionFactory` that doesn't set `enableHostnameVerification`. | ||
*/ | ||
class RabbitMQEnableHostnameVerificationNotSet extends MethodAccess { | ||
RabbitMQEnableHostnameVerificationNotSet() { | ||
this.getMethod().hasName(["useSslProtocol", "setSslContextFactory"]) and | ||
this.getMethod().getDeclaringType() instanceof RabbitMQConnectionFactory and | ||
exists(Variable v | | ||
v.getType() instanceof RabbitMQConnectionFactory and | ||
this.getQualifier() = v.getAnAccess() and | ||
not exists(MethodAccess ma | | ||
ma.getMethod().hasName("enableHostnameVerification") and | ||
ma.getQualifier() = v.getAnAccess() | ||
) | ||
) | ||
} | ||
} | ||
|
||
private class RabbitMQConnectionFactory extends RefType { | ||
RabbitMQConnectionFactory() { this.hasQualifiedName("com.rabbitmq.client", "ConnectionFactory") } | ||
} |
65 changes: 65 additions & 0 deletions
65
java/ql/lib/semmle/code/java/security/UnsafeCertTrustQuery.qll
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
/** Provides taint tracking configurations to be used by unsafe certificate trust queries. */ | ||
|
||
import java | ||
import semmle.code.java.dataflow.TaintTracking | ||
import semmle.code.java.security.UnsafeCertTrust | ||
import semmle.code.java.security.Encryption | ||
|
||
/** | ||
* A taint flow configuration for SSL connections created without a proper certificate trust configuration. | ||
*/ | ||
class SslEndpointIdentificationFlowConfig extends TaintTracking::Configuration { | ||
SslEndpointIdentificationFlowConfig() { this = "SslEndpointIdentificationFlowConfig" } | ||
|
||
override predicate isSource(DataFlow::Node source) { source instanceof SslConnectionInit } | ||
|
||
override predicate isSink(DataFlow::Node sink) { sink instanceof SslConnectionCreation } | ||
|
||
override predicate isSanitizer(DataFlow::Node sanitizer) { | ||
sanitizer instanceof SslUnsafeCertTrustSanitizer | ||
} | ||
} | ||
|
||
/** | ||
* An SSL object that was assigned a safe `SSLParameters` object and can be considered safe. | ||
*/ | ||
private class SslConnectionWithSafeSslParameters extends SslUnsafeCertTrustSanitizer { | ||
SslConnectionWithSafeSslParameters() { | ||
exists(SafeSslParametersFlowConfig config, DataFlow::Node safe, DataFlow::Node sanitizer | | ||
config.hasFlowTo(safe) and | ||
sanitizer = DataFlow::exprNode(safe.asExpr().(Argument).getCall().getQualifier()) and | ||
DataFlow::localFlow(sanitizer, this) | ||
) | ||
} | ||
} | ||
|
||
private class SafeSslParametersFlowConfig extends DataFlow2::Configuration { | ||
SafeSslParametersFlowConfig() { this = "SafeSslParametersFlowConfig" } | ||
|
||
override predicate isSource(DataFlow::Node source) { | ||
exists(MethodAccess ma | | ||
ma instanceof SafeSetEndpointIdentificationAlgorithm and | ||
DataFlow::getInstanceArgument(ma) = source.(DataFlow::PostUpdateNode).getPreUpdateNode() | ||
) | ||
} | ||
|
||
override predicate isSink(DataFlow::Node sink) { | ||
exists(MethodAccess ma, RefType t | t instanceof SSLSocket or t instanceof SSLEngine | | ||
ma.getMethod().hasName("setSSLParameters") and | ||
ma.getMethod().getDeclaringType().getASupertype*() = t and | ||
ma.getArgument(0) = sink.asExpr() | ||
) | ||
} | ||
} | ||
|
||
/** | ||
* A call to `SSLParameters.setEndpointIdentificationAlgorithm` with a non-null and non-empty parameter. | ||
*/ | ||
private class SafeSetEndpointIdentificationAlgorithm extends MethodAccess { | ||
SafeSetEndpointIdentificationAlgorithm() { | ||
this.getMethod().hasName("setEndpointIdentificationAlgorithm") and | ||
this.getMethod().getDeclaringType() instanceof SSLParameters and | ||
not this.getArgument(0) instanceof NullLiteral and | ||
not this.getArgument(0).(CompileTimeConstantExpr).getStringValue() = "" | ||
} | ||
} |
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
<!DOCTYPE qhelp PUBLIC | ||
"-//Semmle//qhelp//EN" | ||
"qhelp.dtd"> | ||
<qhelp> | ||
|
||
<overview> | ||
<p>Java offers two mechanisms for SSL authentication - trust manager and hostname verifier (the later is checked by the <code>java/insecure-hostname-verifier</code> query). The trust manager validates the peer's certificate chain while hostname verification establishes that the hostname in the URL matches the hostname in the server's identification.</p> | ||
<p>When <code>SSLSocket</code> or <code>SSLEngine</code> are created without a secure <code>setEndpointIdentificationAlgorithm</code>, hostname verification is disabled by default.</p> | ||
<p>This query checks whether <code>setEndpointIdentificationAlgorithm</code> is missing, thereby making the application vulnerable to man-in-the-middle attacks. The query also covers insecure configurations of <code>com.rabbitmq.client.ConnectionFactory</code>.</p> | ||
</overview> | ||
|
||
<recommendation> | ||
<p>Validate SSL certificates in SSL authentication.</p> | ||
</recommendation> | ||
|
||
<example> | ||
<p>The following two examples show two ways of configuring SSLSocket/SSLEngine. In the 'BAD' case, | ||
<code>setEndpointIdentificationAlgorithm</code> is not called, thus no hostname verification takes place. In the 'GOOD' case, <code>setEndpointIdentificationAlgorithm</code> is called.</p> | ||
<sample src="UnsafeCertTrust.java" /> | ||
</example> | ||
|
||
<references> | ||
<li> | ||
<a href="https://github.com/OWASP/owasp-mstg/blob/master/Document/0x05g-Testing-Network-Communication.md">Testing Endpoint Identify Verification (MSTG-NETWORK-3)</a>. | ||
</li> | ||
<li> | ||
<a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/javax/net/ssl/SSLParameters.html#setEndpointIdentificationAlgorithm(java.lang.String)">SSLParameters.setEndpointIdentificationAlgorithm documentation</a>. | ||
</li> | ||
<li> | ||
RabbitMQ: | ||
<a href="https://rabbitmq.github.io/rabbitmq-java-client/api/current/com/rabbitmq/client/ConnectionFactory.html#enableHostnameVerification()">ConnectionFactory.enableHostnameVerification documentation</a>. | ||
</li> | ||
<li> | ||
RabbitMQ: | ||
<a href="https://www.rabbitmq.com/ssl.html#java-client">Using TLS in the Java Client</a>. | ||
</li> | ||
<li> | ||
<a href="https://github.com/advisories/GHSA-xvch-r4wf-h8w9">CVE-2018-17187: Apache Qpid Proton-J transport issue with hostname verification</a>. | ||
</li> | ||
<li> | ||
<a href="https://github.com/advisories/GHSA-46j3-r4pj-4835">CVE-2018-8034: Apache Tomcat - host name verification when using TLS with the WebSocket client</a>. | ||
</li> | ||
<li> | ||
<a href="https://github.com/advisories/GHSA-w4g2-9hj6-5472">CVE-2018-11087: Pivotal Spring AMQP vulnerability due to lack of hostname validation</a>. | ||
</li> | ||
<li> | ||
<a href="https://github.com/advisories/GHSA-m9w8-v359-9ffr">CVE-2018-11775: TLS hostname verification issue when using the Apache ActiveMQ Client</a>. | ||
</li> | ||
</references> | ||
</qhelp> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
/** | ||
* @name Unsafe certificate trust | ||
* @description SSLSocket/SSLEngine ignores all SSL certificate validation | ||
* errors when establishing an HTTPS connection, thereby making | ||
* the app vulnerable to man-in-the-middle attacks. | ||
* @kind problem | ||
* @problem.severity warning | ||
* @precision medium | ||
* @id java/unsafe-cert-trust | ||
* @tags security | ||
* external/cwe/cwe-273 | ||
*/ | ||
|
||
import java | ||
import semmle.code.java.security.UnsafeCertTrustQuery | ||
|
||
from Expr unsafeTrust | ||
where | ||
unsafeTrust instanceof RabbitMQEnableHostnameVerificationNotSet or | ||
exists(SslEndpointIdentificationFlowConfig config | | ||
config.hasFlowTo(DataFlow::exprNode(unsafeTrust)) | ||
) | ||
select unsafeTrust, "Unsafe configuration of trusted certificates." |
4 changes: 4 additions & 0 deletions
4
java/ql/src/change-notes/2021-06-28-unsafe-cert-trust-query.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
--- | ||
category: newQuery | ||
--- | ||
* The query "Unsafe certificate trust" (`java/unsafe-cert-trust`) 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 @luchua-bc](https://github.com/github/codeql/pull/3550). |
Oops, something went wrong.