1000 Dutch apps test details
Last year we tested 1000 Dutch apps on how they handle transportation of private data on the network. The research shows that out of 531 apps that handle sensitive data, 45% had a broken SSL/TLS implementation or, even worse, did not use SSL/TLS at all. In this article we'll explain the selection of the apps, the test approach, and detail our test results.
Securify is based in the Netherlands. We wanted to investigate the security of Dutch apps. Selecting Dutch apps is not as trivial as it seems, because apps are not categorized on origin. For example, it is not hard to obtain a list of apps that are popular in the Netherlands. This list however does not merely consist of apps that are built in the Netherlands. We wanted to see how Dutch developers score in general, ranging from major corporations to hobbyists. What followed was a period of Googling, web scraping, filtering duplicates, and so on. Finally we assembled a list of 1000 Dutch apps.
Scope & approach
The tests itself are conducted from the perspective of an opportunistic hacker in which the hacker operates a malicious WiFi access point to which the victim is connected. The hacker then tries to perform a man in the middle against each (HTTP/HTTPS) connection that is created by apps running on the device. Since apps can use different mechanism to create a connection - for example a hybrid app with native components and one or more WebViews - we needed to interact with the app to try to test each connection class. We've looked at automation tools, but finally came to the conclusion that human interaction was the easiest solution for testing these apps. We did use static analysis to perform a quick analysis of the app. For example the use of an insecure onReceivedSslError() method is a clear indication that an app is potentially vulnerable. The test approach used was:
- perform static analysis;
- install the app (automated);
- launch the app;
- interact with app to generate traffic;
- inspect the traffic;
- uninstall the app (automated).
In the case an app requires a logon, only the logon request itself is tested. Behavior of the app after the logon is not tested.
Thinning out their numbers
We started out with 1000 apps. Not every apps creates a network connection, for example a single player game can perfectly run on the device without the exchange of data. In addition, not all data transmitted over the network is sensitive and thus encryption of the data in transport would be overkill from a security standpoint. We removed apps with the following characteristics from our list:
- no network connection;
- no handling of private/sensitive data.
This results in a set of 531 apps that require extra inspection.
Continuing with the remaining 531 apps, we looked at which apps actually used SSL/TLS. We are surprised that 37% of these apps (199 out of 531) did not use SSL/TLS at all. This means that sensitive data is transported by these apps over the internet in clear text.
This leaves 332 apps that have at least the intention of secure communication over the internet. From these remaining apps we discovered that 43 of them had a broken SSL/TLS implementation.
In total we conclude that 45% of the apps that transport sensitive data over the internet do this in an insecure manner. Based on other research results it does appear that Dutch app developers perform better when sending sensitive data over the wire (45% versus 73%).
Our test setup consists of a (rogue) WiFi access point, using airbase-ng, that is connected to the internet. The system running airbase-ng is configured as follows:
- running a DHCP server, the gateway is set to the IP of the AP;
- running an intercepting proxy that can attack SSL/TLS connections; we recommend mitmproxy;
- forwarding TCP connections with source port set to port 80 & 443 to pass through our proxy;
- NAT for all other connections;
- running Wireshark to inspect non-HTTP(s) connections.
Our test devices are configured to use this rogue access point as their main gateway.
The CERT Division provides a virtual machine called CERT Tapioco that is similar to the setup described above with the exception that it does not act as an access point. Instead of airbase-ng a simple home route can be used that is connected to CERT Tapioca. This should be enough reproduce our tests.
What can you do?
Luckily, it is not difficult to install a proper SSL/TLS implementation within your apps. Android has two HTTP clients: HttpURLConnection and Apache HTTP Client. It is recommended to use HttpURLConnection on Android 2.3 and later versions.
URL url = new URL("https://www.android.com/");
HttpsURLConnection urlConnection = (HttpsURLConnection) url.openConnection();
InputStream in = new BufferedInputStream(urlConnection.getInputStream());
The sample above uses the default Android SSL/TLS implementation. This means Android will automatically verify the validity of the certificate that is used when you set up a connection to a server: It will use the global Android certificate store to check whether the certificate has been issued by a trusted certificate authority and check the common name in the certificate against the hostname of the server.
You can bring the security of your SSL/TLS implementation in your app to the next level by implementing certificate pinning. To prevent man in the middle attacks, even when the attacker has access to a valid signing key from a certificate authority (like with the Diginotar hack), you can modify the default behavior of the certificate verification to implement a whitelist with accepted certificates (pinning).
The simplest way to implement pinning in Android is to use a custom certificate store with only the root certificate that signed the server certificate (instead of the system store that is used by default and contains all root certificates that are trusted by Android). In this implementation you are pinning the root certificate (or a self-signed certificate), which in most cases provides enough security (only by gaining access to the private key of a certificate in the specific chain a man in the middle attack is possible). This implementation is currently in use by several banking apps. An example of certificate pinning using a custom key store can be found at the following location: https://developer.android.com/training/articles/security-ssl.html
Another possible implementation is using a custom TrustManager. In the TrustManager you can use hashes of certificates (hardcoded or from a configuration file) in the pinning check when validating the certificate chain. This implementation provides more flexibility, because you can for example add pinning for intermediate certificates or for a leaf. The problem with using this option though is that it is a lot easier to make mistakes in this implementation, possibly resulting in failing (or missing) SSL/TLS checks. So unless you need functionality that cannot be provided by using a custom key store, don’t mess with the TrustManager. On OWASP’s website you can find an example of pinning in Android through a custom X509TrustManager: https://www.owasp.org/index.php/Certificate_and_Public_Key_Pinning#Android