CodeQL documentation

Android missing certificate pinning

ID: java/android/missing-certificate-pinning
Kind: problem
Security severity: 5.9
Severity: warning
Precision: medium
Tags:
   - security
   - external/cwe/cwe-295
Query suites:
   - java-security-extended.qls
   - java-security-and-quality.qls

Click to see the query in the CodeQL repository

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.

Recommendation

The easiest way to implement certificate pinning is to declare your pins in a network-security-config XML file. This will automatically provide certificate pinning for any network connection made by the app.

Another way to implement certificate pinning is to use the `CertificatePinner` class from the `okhttp` library.

A final way to implement certificate pinning is to use a TrustManager, initialized from a KeyStore loaded with only the necessary certificates.

Example

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.

// BAD - By default, this network call does not use certificate pinning
URLConnection conn = new URL("https://example.com").openConnection();
<!-- 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>
// 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());

References

  • © GitHub, Inc.
  • Terms
  • Privacy