mirror of
https://github.com/github/codeql.git
synced 2025-12-21 03:06:31 +01:00
Merge pull request #6171 from atorralba/atorralba/promote-unsafe-certificate-trust
Java: Promote Unsafe certificate trust query from experimental
This commit is contained in:
50
java/ql/src/Security/CWE/CWE-273/UnsafeCertTrust.qhelp
Normal file
50
java/ql/src/Security/CWE/CWE-273/UnsafeCertTrust.qhelp
Normal file
@@ -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>
|
||||
23
java/ql/src/Security/CWE/CWE-273/UnsafeCertTrust.ql
Normal file
23
java/ql/src/Security/CWE/CWE-273/UnsafeCertTrust.ql
Normal file
@@ -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."
|
||||
@@ -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).
|
||||
@@ -1,42 +0,0 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
|
||||
<overview>
|
||||
<p>When SSLSocket or SSLEngine is created without a valid parameter of setEndpointIdentificationAlgorithm, hostname verification is disabled by default.</p>
|
||||
<p>Unsafe implementation of the interface X509TrustManager and SSLSocket/SSLEngine ignores all SSL certificate validation errors when establishing an HTTPS connection, thereby making the app vulnerable to man-in-the-middle attacks.</p>
|
||||
<p>This query checks whether setEndpointIdentificationAlgorithm is missing. The query also covers a special implementation com.rabbitmq.client.ConnectionFactory.</p>
|
||||
</overview>
|
||||
|
||||
<recommendation>
|
||||
<p>Validate SSL certificate in SSL authentication.</p>
|
||||
</recommendation>
|
||||
|
||||
<example>
|
||||
<p>The following two examples show two ways of configuring SSLSocket/SSLEngine. In the 'BAD' case,
|
||||
setEndpointIdentificationAlgorithm is not called, thus no hostname verification takes place. In the 'GOOD' case, setEndpointIdentificationAlgorithm is called.</p>
|
||||
<sample src="UnsafeCertTrust.java" />
|
||||
</example>
|
||||
|
||||
<references>
|
||||
<li>
|
||||
<a href="https://cwe.mitre.org/data/definitions/273.html">CWE-273</a>
|
||||
</li>
|
||||
<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://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>
|
||||
@@ -1,169 +0,0 @@
|
||||
/**
|
||||
* @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.Encryption
|
||||
|
||||
class SSLEngine extends RefType {
|
||||
SSLEngine() { this.hasQualifiedName("javax.net.ssl", "SSLEngine") }
|
||||
}
|
||||
|
||||
class Socket extends RefType {
|
||||
Socket() { this.hasQualifiedName("java.net", "Socket") }
|
||||
}
|
||||
|
||||
class SocketFactory extends RefType {
|
||||
SocketFactory() { this.hasQualifiedName("javax.net", "SocketFactory") }
|
||||
}
|
||||
|
||||
class SSLSocket extends RefType {
|
||||
SSLSocket() { this.hasQualifiedName("javax.net.ssl", "SSLSocket") }
|
||||
}
|
||||
|
||||
/**
|
||||
* has setEndpointIdentificationAlgorithm set correctly
|
||||
*/
|
||||
predicate setEndpointIdentificationAlgorithm(MethodAccess createSSL) {
|
||||
exists(
|
||||
Variable sslo, MethodAccess ma, Variable sslparams //setSSLParameters with valid setEndpointIdentificationAlgorithm set
|
||||
|
|
||||
createSSL = sslo.getAnAssignedValue() and
|
||||
ma.getQualifier() = sslo.getAnAccess() and
|
||||
ma.getMethod().hasName("setSSLParameters") and
|
||||
ma.getArgument(0) = sslparams.getAnAccess() and
|
||||
exists(MethodAccess setepa |
|
||||
setepa.getQualifier() = sslparams.getAnAccess() and
|
||||
setepa.getMethod().hasName("setEndpointIdentificationAlgorithm") and
|
||||
not setepa.getArgument(0) instanceof NullLiteral
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* has setEndpointIdentificationAlgorithm set correctly
|
||||
*/
|
||||
predicate hasEndpointIdentificationAlgorithm(Variable ssl) {
|
||||
exists(
|
||||
MethodAccess ma, Variable sslparams //setSSLParameters with valid setEndpointIdentificationAlgorithm set
|
||||
|
|
||||
ma.getQualifier() = ssl.getAnAccess() and
|
||||
ma.getMethod().hasName("setSSLParameters") and
|
||||
ma.getArgument(0) = sslparams.getAnAccess() and
|
||||
exists(MethodAccess setepa |
|
||||
setepa.getQualifier() = sslparams.getAnAccess() and
|
||||
setepa.getMethod().hasName("setEndpointIdentificationAlgorithm") and
|
||||
not setepa.getArgument(0) instanceof NullLiteral
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Cast of Socket to SSLSocket
|
||||
*/
|
||||
predicate sslCast(MethodAccess createSSL) {
|
||||
exists(Variable ssl, CastExpr ce |
|
||||
ce.getExpr() = createSSL and
|
||||
ce.getControlFlowNode().getASuccessor().(VariableAssign).getDestVar() = ssl and
|
||||
ssl.getType() instanceof SSLSocket //With a type cast `SSLSocket socket = (SSLSocket) socketFactory.createSocket("www.example.com", 443)`
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* SSL object is created in a separate method call or in the same method
|
||||
*/
|
||||
predicate hasFlowPath(MethodAccess createSSL, Variable ssl) {
|
||||
(
|
||||
createSSL = ssl.getAnAssignedValue()
|
||||
or
|
||||
exists(CastExpr ce |
|
||||
ce.getExpr() = createSSL and
|
||||
ce.getControlFlowNode().getASuccessor().(VariableAssign).getDestVar() = ssl //With a type cast like SSLSocket socket = (SSLSocket) socketFactory.createSocket("www.example.com", 443);
|
||||
)
|
||||
)
|
||||
or
|
||||
exists(MethodAccess tranm |
|
||||
createSSL.getEnclosingCallable() = tranm.getMethod() and
|
||||
tranm.getControlFlowNode().getASuccessor().(VariableAssign).getDestVar() = ssl and
|
||||
not setEndpointIdentificationAlgorithm(createSSL) //Check the scenario of invocation before used in the current method
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Not have the SSLParameter set
|
||||
*/
|
||||
predicate hasNoEndpointIdentificationSet(MethodAccess createSSL, Variable ssl) {
|
||||
//No setSSLParameters set
|
||||
hasFlowPath(createSSL, ssl) and
|
||||
not exists(MethodAccess ma |
|
||||
ma.getQualifier() = ssl.getAnAccess() and
|
||||
ma.getMethod().hasName("setSSLParameters")
|
||||
)
|
||||
or
|
||||
//No endpointIdentificationAlgorithm set with setSSLParameters
|
||||
hasFlowPath(createSSL, ssl) and
|
||||
not setEndpointIdentificationAlgorithm(createSSL)
|
||||
}
|
||||
|
||||
/**
|
||||
* The setEndpointIdentificationAlgorithm method of SSLParameters with the ssl engine or socket
|
||||
*/
|
||||
class SSLEndpointIdentificationNotSet extends MethodAccess {
|
||||
SSLEndpointIdentificationNotSet() {
|
||||
(
|
||||
this.getMethod().hasName("createSSLEngine") and
|
||||
this.getMethod().getDeclaringType() instanceof SSLContext //createEngine method of SSLContext
|
||||
or
|
||||
this.getMethod().hasName("createSocket") and
|
||||
this.getMethod().getDeclaringType() instanceof SocketFactory and
|
||||
this.getMethod().getReturnType() instanceof Socket and
|
||||
sslCast(this) //createSocket method of SocketFactory
|
||||
) and
|
||||
exists(Variable ssl |
|
||||
hasNoEndpointIdentificationSet(this, ssl) and //Not set in itself
|
||||
not exists(VariableAssign ar, Variable newSsl |
|
||||
ar.getSource() = this.getCaller().getAReference() and
|
||||
ar.getDestVar() = newSsl and
|
||||
hasEndpointIdentificationAlgorithm(newSsl) //Not set in its caller either
|
||||
)
|
||||
) and
|
||||
not exists(MethodAccess ma | ma.getMethod() instanceof HostnameVerifierVerify) //Reduce false positives since this method access set default hostname verifier
|
||||
}
|
||||
}
|
||||
|
||||
class RabbitMQConnectionFactory extends RefType {
|
||||
RabbitMQConnectionFactory() { this.hasQualifiedName("com.rabbitmq.client", "ConnectionFactory") }
|
||||
}
|
||||
|
||||
/**
|
||||
* The com.rabbitmq.client.ConnectionFactory useSslProtocol method access without enableHostnameVerification
|
||||
*/
|
||||
class RabbitMQEnableHostnameVerificationNotSet extends MethodAccess {
|
||||
RabbitMQEnableHostnameVerificationNotSet() {
|
||||
this.getMethod().hasName("useSslProtocol") 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()
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
from MethodAccess aa
|
||||
where
|
||||
aa instanceof SSLEndpointIdentificationNotSet or
|
||||
aa instanceof RabbitMQEnableHostnameVerificationNotSet
|
||||
select aa, "Unsafe configuration of trusted certificates"
|
||||
@@ -94,18 +94,6 @@ class UnsafeTlsVersion extends StringLiteral {
|
||||
}
|
||||
}
|
||||
|
||||
class SSLParameters extends RefType {
|
||||
SSLParameters() { hasQualifiedName("javax.net.ssl", "SSLParameters") }
|
||||
}
|
||||
|
||||
class SSLSocket extends RefType {
|
||||
SSLSocket() { hasQualifiedName("javax.net.ssl", "SSLSocket") }
|
||||
}
|
||||
|
||||
class SSLServerSocket extends RefType {
|
||||
SSLServerSocket() { hasQualifiedName("javax.net.ssl", "SSLServerSocket") }
|
||||
}
|
||||
|
||||
class SSLEngine extends RefType {
|
||||
SSLEngine() { hasQualifiedName("javax.net.ssl", "SSLEngine") }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user