Last year, Android Nougat (7.0+) introduced the network security config, which can complicate debugging and testing HTTPS calls. Previous versions of Android allowed users to install SSL certificates onto their device that could decrypt all the SSL network traffic on the device. This creates potential for man-in-the-middle attacks, for example using a Charles proxy with SSL proxying enabled. There are a lot of legitimate uses for this kind of man-in-the-middle attack during development. A tester can verify that the advertisement network calls contain the correct metadata, verify that an unexpected behavior was caused by invalid data instead of a client-side bug, or map data feeds to simulate application states.
Nougat’s new network security config complicates this. First, the network security config must be declared in the Android Manifest under like so, android:networkSecurityConfig=”@xml/network_security_config. The network security config has to declare any SSL certificates that the app should trust. This can be your team’s internal certificate authority or a personal certificate for debugging purposes. Apps can also allow any user-installed certificates to be trusted. However, that’s an obvious security concern that undermines the benefits of Nougat’s new approach to security. There’s a tag to allow apps with debuggable=true to define configurations that wouldn’t be allowed in apps released to the Play Store. For example, a debug version of the app could trust all user-installed certificates, while a release version blocks user-installed certificates. Because the Play Store blocks any build with debuggable=true from being submitted, there is no risk in submitting a vulnerable build of the app. Meanwhile the tester or developer can use their personal certificates as they have done with previous versions of Android for debug builds. The config file has the following format and should be placed in the res/xml directory.
<?xml version="1.0" encoding="utf-8"?> <network-security-config> <debug-overrides> <trust-anchors> <certificates src="user"/> </trust-anchors> </debug-overrides> </network-security-config>
If your project is setup with multiple build variants, i.e. production and development variants, then you can be twice as safe. Only declare trust for certificates in a network security config inside the development variant while the production variant has an empty network security config. Then only developmentDebug variants would work with the local certificate, while production or release variants would not trust user-installed certificates.
<?xml version="1.0" encoding="utf-8"?> <network-security-config />
This works for devices where the user can install their own certificate or a shared one. However, some devices like Android TV and Fire TV don’t allow the user to install a certificate. For these devices, using the network security config to include a certificate inside the app is the only way we’re aware of to proxy SSL traffic. The following steps show how to do that with Charles, other proxies should work but I’m not sure what the steps are. These are high level steps, more detailed steps are below.
- Export a Charles certificate as a .p12 with a password.
- Share the .p12 and the password your team.
- Team members should set this certificate as their Charles root certificate.
- Save a .pem file from Charles.
- Place this .pem into the res/raw folder.
- In the network security config add that .pem file as a trust anchor.
The first step is to export a Charles certificate and private key that can be shared with the other developers and testers on your team and with the app. Inside Charles go to “Help” -> “SSL Proxying” -> “Export Charles Root Certificate and Private Key…”, which will prompt you for a password to create a .p12 file. The password will be shared so don’t use your banking password. This .p12 file can be shared internally via emails or uploaded to a secure place where everyone can access it. It’s also smart to include it in the actual project in order to track it with version control. Since this isn’t a file that you want bundled in the APK that actually gets shipped, the codesign directory is a good place to keep your .p12 file.
This is the root certificate for sharing among your entire team. To set up their Charles instance to use the same root certificate, each team member will need to go to “Proxy” -> “SSL Proxy Settings…”, which opens a window with three tabs. The third tab is “Root Certificate”, which has an option to choose a new root certificate. This should be the shared .p12 file from above. This prompts for a password, which is the same password created to export the .p12 file.
Now the entire team is configured to use the same root certificate, but the app isn’t bundling a certificate yet. From the same Charles instance used to export the .p12 file, go to “Help” -> “SSL Proxying” -> “Save Charles Root Certificate…” which generates a .pem file. This is the public certificate which will actually ship inside the app’s APK. Place this into the /raw resources folder. Similar to the previously mentioned strategy where the production variant held an empty network security config, if you have a development build variant, you can place the .pem file into a development/res/raw folder so it doesn’t ship in the production app. Now the network security config can reference this certificate in its trust-anchors like so.
<?xml version="1.0" encoding="utf-8"?> <network-security-config> <debug-overrides> <trust-anchors> <certificates src="@raw/charles_cert"/> <certificates src="user"/> </trust-anchors> </debug-overrides> </network-security-config>
The above configuration trusts any user-installed certificates, but it also bundles a certificate inside the app. Charles can be set up to trust that certificate. This is a lifesaver for platforms like Android TV and Fire TV which don’t allow certificate installation. Since the network security config only works on devices running Nougat or newer versions of Android, this actually won’t work for Fire TV devices, which are running a fork of Lollipop. However, Mark Murphy has provided a backport for network security config!
The network security config requires a bit of setup, but it’s a big improvement for app security. Now developers and QA can have special access during development and testing without compromising the security of the shipped app. Since network traffic should be HTTPS whenever possible, there’s a good chance you’ll need to use the network security config for debugging on Nougat or newer devices. Apps deployed to Android TV and Fire TV can also support SSL proxying by bundling certificates into the APK. Now your team can SSL proxy on Android TV devices to verify analytics metadata, look at ad requests, and check for invalid data!