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' |
|
} |
|
|
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 HttpsURLconnection
Unfortunately 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; |
|
} |
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.