Merge pull request #10971 from joefarebrother/android-certificate-pinning

Java: Add Android missing certificate pinning query (CWE-295)
This commit is contained in:
Tony Torralba
2022-12-20 11:03:16 +01:00
committed by GitHub
86 changed files with 1867 additions and 2617 deletions

View File

@@ -0,0 +1,48 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>
Certificate pinning is the practice of only trusting a specific set of SSL certificates, rather than those that the device trusts by default.
In Android applications, it is reccomended to use certificate pinning when communicating over the network,
in order to minimize the risk of machine-in-the-middle attacks from a compromised CA.
</p>
</overview>
<recommendation>
<p>
The easiest way to implement certificate pinning is to declare your pins in a <code>network-security-config</code> XML file.
This will automatically provide certificate pinning for any network connection made by the app.
</p>
<p>
Another way to implement certificate pinning is to use the `CertificatePinner` class from the `okhttp` library.
</p>
<p>
A final way to implement certificate pinning is to use a <code>TrustManager</code>, initialized from a <code>KeyStore</code> loaded with only the necessary certificates.
</p>
</recommendation>
<example>
<p>
In the first (bad) case below, a network call is performed with no certificate pinning implemented.
The other (good) cases demonstrate the different ways to implement certificate pinning.
</p>
<sample src="AndroidMissingCertificatePinning1.java" />
<sample src="AndroidMissingCertificatePinning2.xml" />
<sample src="AndroidMissingCertificatePinning3.java" />
</example>
<references>
<li>
OWASP Mobile Security: <a href="https://mobile-security.gitbook.io/mobile-security-testing-guide/android-testing-guide/0x05g-testing-network-communication#testing-custom-certificate-stores-and-certificate-pinning-mstg-network-4">Testing Custom Certificate Stores and Certificate Pinning (MSTG-NETWORK-4)</a>.
</li>
<li>
Android Developers: <a href="https://developer.android.com/training/articles/security-config">Network security configuration</a>.
</li>
<li>
OkHttp: <a href="https://square.github.io/okhttp/4.x/okhttp/okhttp3/-certificate-pinner/">CertificatePinner</a>.
</li>
</references>
</qhelp>

View File

@@ -0,0 +1,22 @@
/**
* @name Android missing certificate pinning
* @description Network connections that do not use certificate pinning may allow attackers to eavesdrop on communications.
* @kind problem
* @problem.severity warning
* @security-severity 5.9
* @precision medium
* @id java/android/missing-certificate-pinning
* @tags security
* external/cwe/cwe-295
*/
import java
import semmle.code.java.security.AndroidCertificatePinningQuery
from DataFlow::Node node, string domain, string msg
where
missingPinning(node, domain) and
if domain = ""
then msg = "(no explicitly trusted domains)"
else msg = "(" + domain + " is not trusted by a pin)"
select node, "This network call does not implement certificate pinning. " + msg

View File

@@ -0,0 +1,2 @@
// BAD - By default, this network call does not use certificate pinning
URLConnection conn = new URL("https://example.com").openConnection();

View File

@@ -0,0 +1,21 @@
<!-- GOOD: Certificate pinning implemented via a Network Security Config file -->
<!-- In AndroidManifest.xml -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.app">
<application android:networkSecurityConfig="@xml/NetworkSecurityConfig">
...
</application>
</manifest>
<!-- In res/xml/NetworkSecurityConfig.xml -->
<network-security-config>
<domain-config>
<domain>good.example.com</domain>
<pin-set expiration="2038/1/19">
<pin digest="SHA-256">...</pin>
</pin-set>
</domain-config>
</network-security-config>

View File

@@ -0,0 +1,26 @@
// GOOD: Certificate pinning implemented via okhttp3.CertificatePinner
CertificatePinner certificatePinner = new CertificatePinner.Builder()
.add("example.com", "sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=")
.build();
OkHttpClient client = new OkHttpClient.Builder()
.certificatePinner(certificatePinner)
.build();
client.newCall(new Request.Builder().url("https://example.com").build()).execute();
// GOOD: Certificate pinning implemented via a TrustManager
KeyStore keyStore = KeyStore.getInstance("BKS");
keyStore.load(resources.openRawResource(R.raw.cert), null);
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(keyStore);
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, tmf.getTrustManagers(), null);
URL url = new URL("http://www.example.com/");
HttpsURLConnection urlConnection = (HttpsURLConnection) url.openConnection();
urlConnection.setSSLSocketFactory(sslContext.getSocketFactory());

View File

@@ -0,0 +1,4 @@
---
category: newQuery
---
* Added a new query, `java/android/missing-certificate-pinning`, to find network calls where certificate pinning is not implemented.