-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Java: Promote Unsafe certificate trust query from experimental #6171
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
atorralba
merged 26 commits into
github:main
from
atorralba:atorralba/promote-unsafe-certificate-trust
Jan 20, 2022
Merged
Changes from all commits
Commits
Show all changes
26 commits
Select commit
Hold shift + click to select a range
e0f4c73
Move from experimental
atorralba 4313baf
Big refactor:
atorralba 02d0fa9
Minor changes in QLDocs and a sanitizer's type
atorralba e43fff2
Use InlineExpectationsTest
atorralba 5d4cd70
Adjusted sources and sanitizer of UnsafeCertTrust taint tracking config
atorralba e842acf
Improve qhelp
atorralba 4508945
Fix assumption regarding when an SSLSocket does the TLS handhsake
atorralba 64518bf
Handle a specific pass-by-reference flow issue
atorralba 19d1a78
Generalize sanitizer using local flow
atorralba 9e93aec
Add spurious test case
atorralba 5997b87
Add change note
atorralba c24520c
Adjust qhelp after rebase
atorralba 68fe3dd
Fix conflicts in experimental query
atorralba 698fd64
Adjust test after rebase
atorralba e9712f0
Add missing QLDoc
atorralba 999acb0
Improve qhelp references
atorralba 4d20710
Fix QLDoc
atorralba d9e98ce
Consider setSslContextFactory and fix tests
atorralba 1e2a956
Remove unused stub
atorralba 000a544
Decouple UnsafeCertTrust.qll to reuse the taint tracking configuration
atorralba c16181d
QLDocs
atorralba 9ffc5ab
Update java/ql/src/semmle/code/java/security/UnsafeCertTrustQuery.qll
atorralba 0302058
Apply suggestions from code review
atorralba 101ad77
Move things around after rebase
atorralba e442e50
Apply suggestions from code review
atorralba 695e77a
Simplify isSslSocket predicate
atorralba File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or 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 hidden or 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 hidden or 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 hidden or 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 hidden or 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 hidden or 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 hidden or 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.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.