diff --git a/java/ql/src/experimental/Security/CWE/CWE-295/AlwaysTrueHostnameVerifier.java b/java/ql/src/experimental/Security/CWE/CWE-295/AlwaysTrueHostnameVerifier.java
new file mode 100644
index 000000000000..ccc6f07caaf8
--- /dev/null
+++ b/java/ql/src/experimental/Security/CWE/CWE-295/AlwaysTrueHostnameVerifier.java
@@ -0,0 +1,6 @@
+HostnameVerifier hostnameVerifier = new HostnameVerifier() {
+ @Override
+ public boolean verify(String hostname, SSLSession session) {
+ return true; // BAD, verifies anything as correct
+ }
+};
\ No newline at end of file
diff --git a/java/ql/src/experimental/Security/CWE/CWE-295/AndroidHostnameVerifier.LICENSE b/java/ql/src/experimental/Security/CWE/CWE-295/AndroidHostnameVerifier.LICENSE
new file mode 100644
index 000000000000..0a9014d93e9b
--- /dev/null
+++ b/java/ql/src/experimental/Security/CWE/CWE-295/AndroidHostnameVerifier.LICENSE
@@ -0,0 +1,38 @@
+// Licensed under Apache 2.0 per https://developer.android.com/license
+/*
+Copyright 2020 Android/Android
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+
+// Create an HostnameVerifier that hardwires the expected hostname.
+// Note that is different than the URL's hostname:
+// example.com versus example.org
+HostnameVerifier hostnameVerifier = new HostnameVerifier() {
+ @Override
+ public boolean verify(String hostname, SSLSession session) {
+ HostnameVerifier hv =
+ HttpsURLConnection.getDefaultHostnameVerifier();
+ return hv.verify("example.com", session); // OKAY-ISH: verify that the certificate matches
+ // example.com and only accept example.com as an alternative to example.org
+ // BETTER: fix the underlying configuration problem that causes example.org to present the certificate for the wrong domain.
+ }
+};
+
+// Tell the URLConnection to use our HostnameVerifier
+URL url = new URL("https://example.org/");
+HttpsURLConnection urlConnection =
+ (HttpsURLConnection)url.openConnection();
+urlConnection.setHostnameVerifier(hostnameVerifier);
+InputStream in = urlConnection.getInputStream();
\ No newline at end of file
diff --git a/java/ql/src/experimental/Security/CWE/CWE-295/AndroidHostnameVerifier.java b/java/ql/src/experimental/Security/CWE/CWE-295/AndroidHostnameVerifier.java
new file mode 100644
index 000000000000..9a3e7fe7a303
--- /dev/null
+++ b/java/ql/src/experimental/Security/CWE/CWE-295/AndroidHostnameVerifier.java
@@ -0,0 +1,20 @@
+// Create an HostnameVerifier that hardwires the expected hostname.
+// Note that is different than the URL's hostname:
+// example.com versus example.org
+HostnameVerifier hostnameVerifier = new HostnameVerifier() {
+ @Override
+ public boolean verify(String hostname, SSLSession session) {
+ HostnameVerifier hv =
+ HttpsURLConnection.getDefaultHostnameVerifier();
+ return hv.verify("example.com", session); // OKAY-ISH: verify that the certificate matches
+ // example.com and only accept example.com as an alternative to example.org
+ // BETTER: fix the underlying configuration problem that causes example.org to present the certificate for the wrong domain.
+ }
+};
+
+// Tell the URLConnection to use our HostnameVerifier
+URL url = new URL("https://example.org/");
+HttpsURLConnection urlConnection =
+ (HttpsURLConnection)url.openConnection();
+urlConnection.setHostnameVerifier(hostnameVerifier);
+InputStream in = urlConnection.getInputStream();
\ No newline at end of file
diff --git a/java/ql/src/experimental/Security/CWE/CWE-295/EverythingAcceptingHostnameVerifier.qhelp b/java/ql/src/experimental/Security/CWE/CWE-295/EverythingAcceptingHostnameVerifier.qhelp
new file mode 100644
index 000000000000..899e9335c8a4
--- /dev/null
+++ b/java/ql/src/experimental/Security/CWE/CWE-295/EverythingAcceptingHostnameVerifier.qhelp
@@ -0,0 +1,46 @@
+
+
+If a HostnameVerifier always returns
+Do NOT use an unverifying HostnameVerifier!
+true it will not verify the hostname at all.
+This allows an attacker to perform a man-in-the-middle attack on the application therefore breaking any security Transport Layer Security (TLS) gives.
+
+An Attack would look like this:
+1. The program connects to https://example.com.
+2. The attacker intercepts this connection and presents one of their valid certificates they control, for example one from Let's Encrypt.
+3. Java verifies that the certificate has been issued by a trusted certificate authority.
+4. Java verifies that the certificate has been issued for the host example.com, which will fail because the certificate has been issued for malicious.domain.
+5. Java wants to reject the certificate because the hostname does not match. Before doing this it checks whether there exists a HostnameVerifier.
+6. Your HostnameVerifier is called which returns true for any certificate so also for this one.
+7. Java proceeds with the connection since your HostnameVerifier accepted it.
+8. The attacker can now read the data (Man-in-the-middle) your program sends to https://example.com while the program thinks the connection is secure.
+
+In the first example, the HostnameVerifier always returns true.
+This allows an attacker to perform a man-in-the-middle attack, because any certificate is accepted despite an incorrect hostname.
+
+If a HostnameVerifier incorrectly verifies the hostname it might not verify the hostname at all.
+This allows an attacker to perform a man-in-the-middle attack on the application therefore breaking any security Transport Layer Security (TLS) gives.
+
+Do NOT use an incorrect HostnameVerifier!
+A HostnameVerifier is typically used for either an URL/HttpsURLConnection or a raw SSLSocket.
+A raw SSLSocket does not do any hostname verification so a working hostname verification is important.
+When you use an URL/HttpsURLConnection:
+
+
SSLSocket:
++
SSLParameters.setEndpointIdentificationAlgorithm("HTTPS") to enable hostname verification. This is available starting with Java 7 and Android Api Level 24.HostnameVerifier from HttpsURLConnection.getDefaultHostnameVerifier() to verify the hostname.
+In the first example, the HostnameVerifier verifies the hostname against session.getPeerHost().
+This allows an attacker to perform a man-in-the-middle attack, because getPeerHost() is not authenticated and will always be equal to the hostname.
+
+In the second example, the HostnameVerifier verifies the hostname against different local hosts.
+Comparing hostname against localhost does not verify the certificate. In theory traffic to localhost
+should never leave the host. In practice this may not always be the case due to misconfigurations.
+Comparing hostname against 127.0.0.1 or ::1 does not verify the certificate. These adresses are loopback adresses and traffic
+MUST never leave the host. So this SHOULD be safe, but nevertheless it would be better to use a proper self-signed certificate.
+
+In the third example, we want to connect to the example.org host, but the host presents a certificate for example.com.
+Assuming that example.org and example.com are under our control (and only then this is ok) we can use the default HostnameVerifier
+to verify that the certificate belongs to example.com.
+Note that you should really fix the configuration problem that causes example.org to present the certificate for example.com instead of using this method.
+This method **only** works on Android, on (OpenJDK) Java the default HostnameVerifier will always return false.
+On (OpenJDK) Java you can use this implementation of a working
+hostname verifier instead of the default HostnameVerifier.
+
+In the fourth example, we properly use a raw SSLSocket with enabled SSLParameters.setEndpointIdentificationAlgorithm("HTTPS"), thus verifying hostnames correctly.
+Note that this method is only available with Java 7 and Android Api Level 24.
+The next example shows an alternative for Android Api Levels before 24.
+
+In the fifth example, we properly use a raw SSLSocket and verify the hostname using the HostnameVerifier from HttpsURLConnection.getDefaultHostnameVerifier().
+Note that this is also works on Android Api Levels before 24.
+But also note that this will **not** work on (OpenJDK) Java, because the default HostnameVerifier will always return false.
+