mirror of
https://github.com/github/codeql.git
synced 2026-04-28 02:05:14 +02:00
Added a query for ignored hostname verification
- Added IgnoredHostnameVerification.ql - Added a qhelp file with examples - Added tests
This commit is contained in:
committed by
Artem Smotrakov
parent
2ecf0d3264
commit
e11cb943a6
@@ -0,0 +1,8 @@
|
||||
SSLSocket socket = (SSLSocket) SSLSocketFactory.getDefault().createSocket(host, port);
|
||||
socket.startHandshake();
|
||||
boolean successful = verifier.verify(host, socket.getSession());
|
||||
if (!successful) {
|
||||
socket.close();
|
||||
throw new SSLException("Oops! Hostname verification failed!");
|
||||
}
|
||||
return socket;
|
||||
@@ -0,0 +1,4 @@
|
||||
SSLSocket socket = (SSLSocket) SSLSocketFactory.getDefault().createSocket(host, port);
|
||||
socket.startHandshake();
|
||||
verifier.verify(host, socket.getSession());
|
||||
return socket;
|
||||
@@ -0,0 +1,42 @@
|
||||
<!DOCTYPE qhelp PUBLIC "-//Semmle//qhelp//EN" "qhelp.dtd">
|
||||
<qhelp>
|
||||
|
||||
<overview>
|
||||
<p>
|
||||
The method <code>HostnameVerifier.verify()</code> checks that the hostname from the server's certificate
|
||||
matches the server hostname after an HTTPS connection is established.
|
||||
The method returns true if the hostname is acceptable and false otherwise. The contract of the method
|
||||
does not require it to throw an exception if the verification failed.
|
||||
Therefore, a caller has to check the result and drop the connection if the hostname verification failed.
|
||||
Otherwise, an attacker may be able to implement a man-in-the-middle attack and impersonate the server.
|
||||
</p>
|
||||
</overview>
|
||||
|
||||
<recommendation>
|
||||
<p>
|
||||
Always check the result of <code>HostnameVerifier.verify()</code> and drop the connection
|
||||
if the method returns false.
|
||||
</p>
|
||||
</recommendation>
|
||||
|
||||
<example>
|
||||
<p>
|
||||
In the following example, the method <code>HostnameVerifier.verify()</code> but its result is ignored.
|
||||
As a result, no hostname verification actually happens.
|
||||
</p>
|
||||
<sample src="IgnoredHostnameVerification.java" />
|
||||
|
||||
<p>
|
||||
In the next example, the result of the <code>HostnameVerifier.verify()</code> method is checked
|
||||
and an exeption is thrown if it the verification failed.
|
||||
</p>
|
||||
<sample src="CheckedHostnameVerification.java" />
|
||||
</example>
|
||||
|
||||
<references>
|
||||
<li>
|
||||
Java API Specification:
|
||||
<a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/javax/net/ssl/HostnameVerifier.html#verify(java.lang.String,javax.net.ssl.SSLSession)">HostnameVerifier.veify() method</a>.
|
||||
</li>
|
||||
</references>
|
||||
</qhelp>
|
||||
@@ -0,0 +1,55 @@
|
||||
/**
|
||||
* @name Ignored result of hostname verification
|
||||
* @description The method HostnameVerifier.verify() returns a result of hostname verification.
|
||||
* A caller has to check the result and drop the connection if the verification failed.
|
||||
* @kind problem
|
||||
* @problem.severity error
|
||||
* @precision medium
|
||||
* @id java/ignored-hostname-verification
|
||||
* @tags security
|
||||
* external/cwe/cwe-295
|
||||
*/
|
||||
|
||||
import java
|
||||
import semmle.code.java.controlflow.Guards
|
||||
import semmle.code.java.dataflow.DataFlow
|
||||
|
||||
private class HostnameVerificationCall extends MethodAccess {
|
||||
HostnameVerificationCall() {
|
||||
getMethod()
|
||||
.getDeclaringType()
|
||||
.getASupertype*()
|
||||
.hasQualifiedName("javax.net.ssl", "HostnameVerifier") and
|
||||
getMethod().hasStringSignature("verify(String, SSLSession)")
|
||||
}
|
||||
|
||||
predicate ignored() {
|
||||
not exists(
|
||||
DataFlow::Node source, DataFlow::Node sink, CheckFailedHostnameVerificationConfig config
|
||||
|
|
||||
this = source.asExpr() and config.hasFlow(source, sink)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private class CheckFailedHostnameVerificationConfig extends DataFlow::Configuration {
|
||||
CheckFailedHostnameVerificationConfig() { this = "CheckFailedHostnameVerificationConfig" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) {
|
||||
source.asExpr() instanceof HostnameVerificationCall
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
exists(Guard guard, ThrowStmt throwStmt |
|
||||
guard.controls(throwStmt.getBasicBlock(), _) and
|
||||
(
|
||||
guard.(EqualityTest).getAnOperand() = sink.asExpr() or
|
||||
guard.(HostnameVerificationCall) = sink.asExpr()
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
from HostnameVerificationCall verification
|
||||
where verification.ignored()
|
||||
select verification, "Ignored result of hostname verification."
|
||||
@@ -0,0 +1,3 @@
|
||||
| IgnoredHostnameVerification.java:15:5:15:46 | verify(...) | Ignored result of hostname verification. |
|
||||
| IgnoredHostnameVerification.java:25:22:25:63 | verify(...) | Ignored result of hostname verification. |
|
||||
| IgnoredHostnameVerification.java:36:22:36:63 | verify(...) | Ignored result of hostname verification. |
|
||||
@@ -0,0 +1,89 @@
|
||||
import java.io.IOException;
|
||||
import javax.net.ssl.HostnameVerifier;
|
||||
import javax.net.ssl.SSLException;
|
||||
import javax.net.ssl.SSLSocket;
|
||||
import javax.net.ssl.SSLSocketFactory;
|
||||
|
||||
public class IgnoredHostnameVerification {
|
||||
|
||||
// BAD: ignored result of HostnameVerifier.verify()
|
||||
public static SSLSocket connectWithIgnoredHostnameVerification(
|
||||
String host, int port, HostnameVerifier verifier) throws IOException {
|
||||
|
||||
SSLSocket socket = (SSLSocket) SSLSocketFactory.getDefault().createSocket(host, port);
|
||||
socket.startHandshake();
|
||||
verifier.verify(host, socket.getSession());
|
||||
return socket;
|
||||
}
|
||||
|
||||
// BAD: ignored result of HostnameVerifier.verify()
|
||||
public static SSLSocket connectAndOnlyPrintResultOfHostnameVerification(
|
||||
String host, int port, HostnameVerifier verifier) throws IOException {
|
||||
|
||||
SSLSocket socket = (SSLSocket) SSLSocketFactory.getDefault().createSocket(host, port);
|
||||
socket.startHandshake();
|
||||
boolean result = verifier.verify(host, socket.getSession());
|
||||
System.out.println("Result of hostname verification: " + result);
|
||||
return socket;
|
||||
}
|
||||
|
||||
// BAD: ignored result of HostnameVerifier.verify()
|
||||
public static SSLSocket connectAndOnlyPrintFailureOfHostnameVerification(
|
||||
String host, int port, HostnameVerifier verifier) throws IOException {
|
||||
|
||||
SSLSocket socket = (SSLSocket) SSLSocketFactory.getDefault().createSocket(host, port);
|
||||
socket.startHandshake();
|
||||
boolean failed = verifier.verify(host, socket.getSession());
|
||||
if (failed) {
|
||||
System.out.println("Hostname verification failed");
|
||||
}
|
||||
|
||||
return socket;
|
||||
}
|
||||
|
||||
// GOOD: connect and check result of HostnameVerifier.verify()
|
||||
public static SSLSocket connectWithHostnameVerification01(
|
||||
String host, int port, HostnameVerifier verifier) throws IOException {
|
||||
|
||||
SSLSocket socket = (SSLSocket) SSLSocketFactory.getDefault().createSocket(host, port);
|
||||
socket.startHandshake();
|
||||
boolean successful = verifier.verify(host, socket.getSession());
|
||||
if (successful == false) {
|
||||
socket.close();
|
||||
throw new SSLException("Oops! Hostname verification failed!");
|
||||
}
|
||||
|
||||
return socket;
|
||||
}
|
||||
|
||||
// GOOD: connect and check result of HostnameVerifier.verify()
|
||||
public static SSLSocket connectWithHostnameVerification02(
|
||||
String host, int port, HostnameVerifier verifier) throws IOException {
|
||||
|
||||
SSLSocket socket = (SSLSocket) SSLSocketFactory.getDefault().createSocket(host, port);
|
||||
socket.startHandshake();
|
||||
boolean successful = verifier.verify(host, socket.getSession());
|
||||
if (!successful) {
|
||||
socket.close();
|
||||
throw new SSLException("Oops! Hostname verification failed!");
|
||||
}
|
||||
|
||||
return socket;
|
||||
}
|
||||
|
||||
// GOOD: connect and check result of HostnameVerifier.verify()
|
||||
public static SSLSocket connectWithHostnameVerification03(
|
||||
String host, int port, HostnameVerifier verifier) throws IOException {
|
||||
|
||||
SSLSocket socket = (SSLSocket) SSLSocketFactory.getDefault().createSocket(host, port);
|
||||
socket.startHandshake();
|
||||
boolean successful = verifier.verify(host, socket.getSession());
|
||||
if (successful) {
|
||||
return socket;
|
||||
}
|
||||
|
||||
socket.close();
|
||||
throw new SSLException("Oops! Hostname verification failed!");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
experimental/Security/CWE/CWE-297/IgnoredHostnameVerification.ql
|
||||
Reference in New Issue
Block a user