-
Notifications
You must be signed in to change notification settings - Fork 2k
Java: CWE-273 Unsafe certificate trust #3550
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
Merged
Changes from 1 commit
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
6d1ba3f
Java: CWE-273 Unsafe certificate trust
luchua-bc 104f1c3
Add validation query for SSL Engine/Socket and com.rabbitmq.client.Co…
luchua-bc deabfe6
Adjust id tag and fix ending line error
luchua-bc f8c4947
Fix ending line error
luchua-bc 0779aab
Clean up the QL code
luchua-bc 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
Next
Next commit
Java: CWE-273 Unsafe certificate trust
- Loading branch information
commit 6d1ba3f8997a633af8fe93fe3c6e53e94966b5ef
There are no files selected for viewing
68 changes: 68 additions & 0 deletions
68
java/ql/src/experimental/Security/CWE/CWE-273/UnsafeCertTrust.java
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,68 @@ | ||
| public static void main(String[] args) { | ||
| { | ||
| HostnameVerifier verifier = new HostnameVerifier() { | ||
| @Override | ||
| public boolean verify(String hostname, SSLSession session) { | ||
| try { //GOOD: verify the certificate | ||
| Certificate[] certs = session.getPeerCertificates(); | ||
| X509Certificate x509 = (X509Certificate) certs[0]; | ||
| check(new String[]{host}, x509); | ||
| return true; | ||
| } catch (SSLException e) { | ||
| return false; | ||
| } | ||
| } | ||
| }; | ||
| HttpsURLConnection.setDefaultHostnameVerifier(verifier); | ||
| } | ||
|
|
||
| { | ||
| HostnameVerifier verifier = new HostnameVerifier() { | ||
| @Override | ||
| public boolean verify(String hostname, SSLSession session) { | ||
| return true; // BAD: accept even if the hostname doesn't match | ||
| } | ||
| }; | ||
| HttpsURLConnection.setDefaultHostnameVerifier(verifier); | ||
| } | ||
|
|
||
| { | ||
| X509TrustManager trustAllCertManager = new X509TrustManager() { | ||
| @Override | ||
| public void checkClientTrusted(final X509Certificate[] chain, final String authType) | ||
| throws CertificateException { | ||
| } | ||
|
|
||
| @Override | ||
| public void checkServerTrusted(final X509Certificate[] chain, final String authType) | ||
| throws CertificateException { | ||
| // BAD: trust any server cert | ||
| } | ||
|
|
||
| @Override | ||
| public X509Certificate[] getAcceptedIssuers() { | ||
| return null; //BAD: doesn't check cert issuer | ||
| } | ||
| }; | ||
| } | ||
|
|
||
| { | ||
| X509TrustManager trustCertManager = new X509TrustManager() { | ||
| @Override | ||
| public void checkClientTrusted(final X509Certificate[] chain, final String authType) | ||
| throws CertificateException { | ||
| } | ||
|
|
||
| @Override | ||
| public void checkServerTrusted(final X509Certificate[] chain, final String authType) | ||
| throws CertificateException { | ||
| pkixTrustManager.checkServerTrusted(chain, authType); //GOOD: validate the server cert | ||
| } | ||
|
|
||
| @Override | ||
| public X509Certificate[] getAcceptedIssuers() { | ||
| return new X509Certificate[0]; //GOOD: Validate the cert issuer | ||
| } | ||
| }; | ||
| } | ||
| } |
33 changes: 33 additions & 0 deletions
33
java/ql/src/experimental/Security/CWE/CWE-273/UnsafeCertTrust.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,33 @@ | ||
| <!DOCTYPE qhelp PUBLIC | ||
| "-//Semmle//qhelp//EN" | ||
| "qhelp.dtd"> | ||
| <qhelp> | ||
|
|
||
| <overview> | ||
| <p>Java offers two mechanisms for SSL authentication - trust manager and hostname verifier. 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>Unsafe implementation of the interface X509TrustManager and HostnameVerifier 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 trust manager is set to trust all certificates or the hostname verifier is turned off.</p> | ||
| </overview> | ||
|
|
||
| <recommendation> | ||
| <p>Validate SSL certificate in SSL authentication.</p> | ||
| </recommendation> | ||
|
|
||
| <example> | ||
| <p>The following two examples show two ways of configuring X509 trust cert manager and hostname verifier. In the 'BAD' case, | ||
| no validation is performed thus any certificate is trusted. In the 'GOOD' case, the proper validation is performed.</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://support.google.com/faqs/answer/6346016?hl=en">How to fix apps containing an unsafe implementation of TrustManager</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> | ||
| </references> | ||
| </qhelp> |
95 changes: 95 additions & 0 deletions
95
java/ql/src/experimental/Security/CWE/CWE-273/UnsafeCertTrust.ql
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,95 @@ | ||
| /** | ||
| * @id java/unsafe-cert-trust | ||
| * @name Unsafe implementation of trusting any certificate in SSL configuration | ||
| * @description Unsafe implementation of the interface X509TrustManager and HostnameVerifier ignores all SSL certificate validation errors when establishing an HTTPS connection, thereby making the app vulnerable to man-in-the-middle attacks. | ||
| * @kind problem | ||
| * @tags security | ||
| * external/cwe-273 | ||
| */ | ||
|
|
||
| import java | ||
| import semmle.code.java.security.Encryption | ||
| import semmle.code.java.dataflow.DataFlow | ||
| import DataFlow | ||
|
|
||
| /** | ||
| * X509TrustManager class that blindly trusts all certificates in server SSL authentication | ||
| */ | ||
| class X509TrustAllManager extends RefType { | ||
| X509TrustAllManager() { | ||
| this.getASupertype*() instanceof X509TrustManager and | ||
| exists(Method m1 | | ||
| m1.getDeclaringType() = this and | ||
| m1.hasName("checkServerTrusted") and | ||
| m1.getBody().getNumStmt() = 0 | ||
| ) and | ||
| exists(Method m2, ReturnStmt rt2 | | ||
| m2.getDeclaringType() = this and | ||
| m2.hasName("getAcceptedIssuers") and | ||
| rt2.getEnclosingCallable() = m2 and | ||
| rt2.getResult() instanceof NullLiteral | ||
| ) | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * The init method of SSLContext with the trust all manager, which is sslContext.init(..., serverTMs, ...) | ||
| */ | ||
| class X509TrustAllManagerInit extends MethodAccess { | ||
| X509TrustAllManagerInit() { | ||
| this.getMethod().hasName("init") and | ||
| this.getMethod().getDeclaringType() instanceof SSLContext and //init method of SSLContext | ||
| ( | ||
| exists(ArrayInit ai | | ||
| ai.getInit(0).(VarAccess).getVariable().getInitializer().getType().(Class).getASupertype*() | ||
| instanceof X509TrustAllManager //Scenario of context.init(null, new TrustManager[] { TRUST_ALL_CERTIFICATES }, null); | ||
| ) | ||
| or | ||
| exists(Variable v, ArrayInit ai | | ||
| this.getArgument(1).(VarAccess).getVariable() = v and | ||
| ai.getParent() = v.getAnAccess().getVariable().getAnAssignedValue() and | ||
|
luchua-bc marked this conversation as resolved.
Outdated
|
||
| ai.getInit(0).getType().(Class).getASupertype*() instanceof X509TrustAllManager //Scenario of context.init(null, serverTMs, null); | ||
| ) | ||
| ) | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * HostnameVerifier class that allows a certificate whose CN (Common Name) does not match the host name in the URL | ||
| */ | ||
| class TrustAllHostnameVerifier extends RefType { | ||
| TrustAllHostnameVerifier() { | ||
| this.getASupertype*() instanceof HostnameVerifier and | ||
| exists(Method m, ReturnStmt rt | | ||
| m.getDeclaringType() = this and | ||
| m.hasName("verify") and | ||
| rt.getEnclosingCallable() = m and | ||
| rt.getResult().(BooleanLiteral).getBooleanValue() = true | ||
| ) | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * The setDefaultHostnameVerifier method of HttpsURLConnection with the trust all configuration | ||
| */ | ||
| class TrustAllHostnameVerify extends MethodAccess { | ||
| TrustAllHostnameVerify() { | ||
| this.getMethod().hasName("setDefaultHostnameVerifier") and | ||
| this.getMethod().getDeclaringType() instanceof HttpsURLConnection and //httpsURLConnection.setDefaultHostnameVerifier method | ||
| ( | ||
| exists(NestedClass nc | | ||
| nc.getASupertype*() instanceof TrustAllHostnameVerifier and | ||
| this.getArgument(0).getType() = nc //Scenario of HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {...}); | ||
| ) | ||
| or | ||
| exists(Variable v | | ||
| this.getArgument(0).(VarAccess).getVariable() = v.getAnAccess().getVariable() and | ||
|
luchua-bc marked this conversation as resolved.
Outdated
|
||
| v.getInitializer().getType() instanceof TrustAllHostnameVerifier //Scenario of HttpsURLConnection.setDefaultHostnameVerifier(verifier); | ||
| ) | ||
| ) | ||
| } | ||
| } | ||
|
|
||
| from MethodAccess aa | ||
| where aa instanceof TrustAllHostnameVerify or aa instanceof X509TrustAllManagerInit | ||
| select aa, "Unsafe configuration of trusted certificates" | ||
4 changes: 4 additions & 0 deletions
4
java/ql/test/experimental/query-tests/security/CWE-273/UnsafeCertTrust.expected
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 @@ | ||
| | UnsafeCertTrustTest.java:19:4:19:74 | init(...) | Unsafe configuration of trusted certificates | | ||
| | UnsafeCertTrustTest.java:34:4:34:38 | init(...) | Unsafe configuration of trusted certificates | | ||
| | UnsafeCertTrustTest.java:47:3:52:4 | setDefaultHostnameVerifier(...) | Unsafe configuration of trusted certificates | | ||
| | UnsafeCertTrustTest.java:65:3:65:57 | setDefaultHostnameVerifier(...) | Unsafe configuration of trusted certificates | |
1 change: 1 addition & 0 deletions
1
java/ql/test/experimental/query-tests/security/CWE-273/UnsafeCertTrust.qlref
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 @@ | ||
| experimental/Security/CWE/CWE-273/UnsafeCertTrust.ql |
110 changes: 110 additions & 0 deletions
110
java/ql/test/experimental/query-tests/security/CWE-273/UnsafeCertTrustTest.java
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,110 @@ | ||
| import javax.net.ssl.HostnameVerifier; | ||
| import javax.net.ssl.HttpsURLConnection; | ||
| import javax.net.ssl.SSLSession; | ||
| import javax.net.ssl.SSLContext; | ||
| import javax.net.ssl.SSLSocketFactory; | ||
| import javax.net.ssl.TrustManager; | ||
| import javax.net.ssl.X509TrustManager; | ||
| import java.security.cert.CertificateException; | ||
| import java.security.cert.X509Certificate; | ||
|
|
||
| public class UnsafeCertTrustTest { | ||
|
|
||
| /** | ||
| * Test the implementation of trusting all server certs as a variable | ||
| */ | ||
| public SSLSocketFactory testTrustAllCertManager() { | ||
| try { | ||
| final SSLContext context = SSLContext.getInstance("SSL"); | ||
| context.init(null, new TrustManager[] { TRUST_ALL_CERTIFICATES }, null); | ||
| final SSLSocketFactory socketFactory = context.getSocketFactory(); | ||
| return socketFactory; | ||
| } catch (final Exception x) { | ||
| throw new RuntimeException(x); | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Test the implementation of trusting all server certs as an anonymous class | ||
| */ | ||
| public SSLSocketFactory testTrustAllCertManagerOfVariable() { | ||
| try { | ||
| SSLContext context = SSLContext.getInstance("SSL"); | ||
| TrustManager[] serverTMs = new TrustManager[] { new X509TrustAllManager() }; | ||
| context.init(null, serverTMs, null); | ||
|
|
||
| final SSLSocketFactory socketFactory = context.getSocketFactory(); | ||
| return socketFactory; | ||
| } catch (final Exception x) { | ||
| throw new RuntimeException(x); | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Test the implementation of trusting all hostnames as an anonymous class | ||
| */ | ||
| public void testTrustAllHostnameOfAnonymousClass() { | ||
| HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() { | ||
| @Override | ||
| public boolean verify(String hostname, SSLSession session) { | ||
| return true; // Noncompliant | ||
| } | ||
| }); | ||
| } | ||
|
|
||
| /** | ||
| * Test the implementation of trusting all hostnames as a variable | ||
| */ | ||
| public void testTrustAllHostnameOfVariable() { | ||
| HostnameVerifier verifier = new HostnameVerifier() { | ||
| @Override | ||
| public boolean verify(String hostname, SSLSession session) { | ||
| return true; // Noncompliant | ||
| } | ||
| }; | ||
| HttpsURLConnection.setDefaultHostnameVerifier(verifier); | ||
| } | ||
|
|
||
| private static final X509TrustManager TRUST_ALL_CERTIFICATES = new X509TrustManager() { | ||
| @Override | ||
| public void checkClientTrusted(final X509Certificate[] chain, final String authType) | ||
| throws CertificateException { | ||
| } | ||
|
|
||
| @Override | ||
| public void checkServerTrusted(final X509Certificate[] chain, final String authType) | ||
| throws CertificateException { | ||
| // Noncompliant | ||
| } | ||
|
|
||
| @Override | ||
| public X509Certificate[] getAcceptedIssuers() { | ||
| return null; // Noncompliant | ||
| } | ||
| }; | ||
|
|
||
| private class X509TrustAllManager implements X509TrustManager { | ||
| @Override | ||
| public void checkClientTrusted(final X509Certificate[] chain, final String authType) | ||
| throws CertificateException { | ||
| } | ||
|
|
||
| @Override | ||
| public void checkServerTrusted(final X509Certificate[] chain, final String authType) | ||
| throws CertificateException { | ||
| // Noncompliant | ||
| } | ||
|
|
||
| @Override | ||
| public X509Certificate[] getAcceptedIssuers() { | ||
| return null; // Noncompliant | ||
| } | ||
| }; | ||
|
|
||
| public static final HostnameVerifier ALLOW_ALL_HOSTNAME_VERIFIER = new HostnameVerifier() { | ||
| @Override | ||
| public boolean verify(String hostname, SSLSession session) { | ||
| return true; // Noncompliant | ||
| } | ||
| }; | ||
| } |
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.