We're Hiring!

Unintentional Unpinning with Firebase

firebase-logo

Google's Firebase provides comprehensive set of analytics services for developers to integrate with their apps. On Android the basic functionality is enabled simply by integrating the desired plugins. No code changes required.

apply plugin: 'com.android.application'
apply plugin: 'com.google.firebase.firebase-perf'
dependencies {
compile 'com.google.firebase:firebase-perf:10.2.6'
}
view rawapp.gradle hosted with ❤ by GitHub

 

In May Google rolled out Firebase Performance Metrics. This new capability allows developers to capture various performance measurements from their app and, out of the box, provides information about network performance for connections made with HttpsURLconnectionUnfortunately there was a subtle bug with real security implications.

Android Certificate Pinning

If the data transmitted between your app and API is valuable then it needs to be sent over a HTTPS connection protected with Certificate Pinning otherwise it can be easily intercepted and decrypted.

On Android certificate pinning can be implemented by registering a custom TrustManagerFactory and/or HostnameVerifier with the HttpsURLconnection you are using to connect with your API. These methods are called when the app is negotiating the HTTPS connection and if the certificate checks fail then an SSLPeerUnverifiedException is thrown. The Android documentation discusses how this works at length. If you are using OkHttp3 then things are different and you are not impacted by this issue since OkHttp uses it's own HTTPS stack (although it has had its own issues).

Firebase Unpinning

The Firebase performance monitoring plugin appears to wrap the HttpsURLconnection connection object in your app in order to capture information about its use. Unfortunately the initial implementation does not appear to correctly handle many of the setters used to customize the connection. In particular the values set via setHostnameVerifer() and setSSLSocketFactory() methods are not passed down to the underlying connection when connect() is called even though the values are correctly set on the top level object and can be fetched back again with the corresponding getters.

The result of this is essentially the same as someone deliberately unpinning your app on a device leaving it vulnerable to Man in the Middle attacks.

Detection and Fix

Fortunately the issue was quickly spotted and fixed by Google. So updating to the latest version of the SDK will solve the problem. The affected versions are 10.2.6 and 11.0.0 with the fix being in place from 11.0.2 onwards. Simple change the compile 'com.google.firebase:firebase-perf:XX.X.X' line in your app/build.gradle to 11.0.2 or higher.

You can confirm if the issue exists in your app by running the following method. Same thing can be used to check for general unpinning.

/**
* Logs if HttpsURLConnection.setHostnameVerifier() is working or not.
* Also returns boolean to that effect.
*/
static boolean testHostnameVerifier() {
boolean isWorking = false;
HttpsURLConnection connection = null;
try {
URL aUrl = new URL("https://firebase.google.com");
// Get our HTTPS connection object
connection = (HttpsURLConnection) aUrl.openConnection();
// Register a hostname verifier which always fails
connection.setHostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession session) {
Log.i("testHostnameVerifier", "CALLED");
return false;
}
});
connection.connect();
// Should never get here with the HostnameVerifier we just set.
Log.i("testHostnameVerifier", "HostnameVerifer NOT WORKING");
} catch (SSLPeerUnverifiedException ex) {
// This exception happens with hostname verifier returns false
Log.i("testHostnameVerifier", "HostnameVerifer IS WORKING");
isWorking = true;
} catch(Exception ex) {
Log.e("testHostnameVerifier", ex.getMessage());
}
if(connection != null)
connection.disconnect();
return isWorking;
}
view rawunpinned.java hosted with ❤ by GitHub

 

Since this is an SDK issue any released apps with the issue will be open to attack. To secure your customer's data, forcing an upgrade to the fixed version of your app will resolve the issue. A more complex problem is that MitM attacks can be used to reverse engineer your API leaving it open to abuse. When updating an app you might also consider improving mobile security by changing the API key and monitoring accesses from the old app version for evidence of bot activity.

 

Barry O'Rourke