This repository contains the files you need to run the demos for our blog post series on TLS certificate checking in Android apps. The first post covers common implementation errors and the second one then explains how you can securely configure TLS connections even in cases when you have to deviate from the default behavior. There are two parts to the repo:
- Example app: In app/ you will find the full AndroidStudio project for the example app that showcases different TLS checking implementations.
- Docker setup: By running setup.sh you prepare a Docker environment consisting of several containers: An Android emulator is spawned and a web frontend to interact with it is made available on https://localhost (Note that this frontend uses a self-signed certificate). Additionally, an example web server container is created, which will be used as the backend for the demo scenarios. The last part of the setup is an attacker container, through which you will be able to interactively intercept web traffic between backend server and Android emulator.
The backend serves a simple HTML website over HTTPs. This mimicks the situation where sensitive data is provided over a secure connection. The catch is that the certificate it uses (see backend/nginx-certs/) has not been issued by a globally trusted CA but rather by a custom one.
The Android app in app/ fetches the data provided by this server and displays it to the user. In order to make Android accept the custom certificate, the default certificate checking mechanism needs to be modified. To showcase different insecure ways of doing so, the app consists of several tabs, where each fetches the data using a different workaround commonly found online. You can check out the corresponding source code in WebViewFragment.kt.
In the first blog post we cover three different types of implementation errors:
- WebView ignores all SSL errors: See setupInsecureWebView()
- Malfunctioning X509TrustManager Implementations: See setupInsecureTrustManager()
- Disabled Host Name Checks: See setupInsecureHostnameVerifier()
The second blog post explains how to configure non-standard certificate checking behaviors in a secure way:
- Allowing Custom Certificate Authorities:
- SDK 24 And Newer: See setupNetworkSecurityConfig() and network_security_config.xml
- Older Versions: See setupCustomCaLegacy() and setupCustomCaLegacyWebview()
- Certificate Pinning:
- SDK 24 And Newer: See setupNetworkSecurityConfig() and network_security_config.xml
- Older Versions: See setupPinningLegacy() and setupPinningLegacyWebview()
This app will be installed to a containerized Android emulator that lives in the same virtual network as the backend server and the attacker. Setting this network up is explained in the next section.
In order to launch the demo environment, you will need to have docker-compose
installed, as well as Python3, NodeJS and npm. Also make sure to have the Android SDK installed (SDK platform version 31).
The ANDROID_SDK_ROOT
environment variable needs to point to its installation directory, usually ~/Android/Sdk
.
All other necessary dependencies will be downloaded automatically.
Running setup.sh will get the necessary files to set up the Docker containers, which may take a while, depending on your system performance and Internet speed. Afterwards you can launch the containers with run.sh. This script takes care of several things:
-
The Android emulator will be booted and a web interface to interact with it is made available on https://localhost (Note that the website uses a self-signed certificate). Login with username
user
and passwordpass
. Then you should see the emulator screen, with which you can interact using your mouse. -
In the meantime, the example app is compiled and once the emulator is fully booted up it is installed and launched automatically.
-
Once the app is running, a bash shell is opened on the attacker container so that you can interactively experiment with the man-in-the-middle setup. As a quick start, you can simply execute the start.sh script that you will find in the current working directory where the shell was spawned (
/eve_files
on the container).This script sets up the attacker proxy using the mitmproxy tool without needing any user input. You can then observe intercepted traffic in the console that will show up. To exit the console and stop the attack, simply press Ctrl+C and confirm. Should you want to deviate from the default attacker script, feel free to inspect start.sh and the associated proxy.py file.
If you would like to experiment with the certificate pinning implementations,
start.sh
allows you to pass--custom-ca
, which will instructmitmproxy
to use a certificate that was signed by the same custom CA that the example server uses. This mimics the situation that the attacker is indeed able to get a valid certificate for your domain, which would be trusted under normal circumstances. The additional certificate pinning step however is able to successfully detect the attack and refuse the connection. -
After you are done exploring the demos, simply exit the attacker shell as usual (Ctrl+D or typing
exit
). This will automatically shut down the containers in a clean way.