mirror of
https://github.com/github/codeql.git
synced 2026-04-28 02:05:14 +02:00
Merge pull request #10971 from joefarebrother/android-certificate-pinning
Java: Add Android missing certificate pinning query (CWE-295)
This commit is contained in:
@@ -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>
|
||||
@@ -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
|
||||
@@ -0,0 +1,2 @@
|
||||
// BAD - By default, this network call does not use certificate pinning
|
||||
URLConnection conn = new URL("https://example.com").openConnection();
|
||||
@@ -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>
|
||||
@@ -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());
|
||||
@@ -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.
|
||||
Reference in New Issue
Block a user