The cleartextTrafficPermitted flag is one of the options in Android’s Network Security Configuration file. The online documentation (https://developer.android.com/training/articles/security-config) explains that from Android 9 (API level 28) and higher, it will be set by default to false and it is intended to prevent insecure communication attempts using clear-text HTTP originating from Android applications.
OK, so what does this actually mean? Will all apps that run on the Android 9 platform with a default configuration be protected? Do these flags instruct the system to prohibit any kind of non-secure TCP communications or does it only apply to HTTP? What about 3rd party libraries such as OkHttp, async-http-client, dubbo? What about WebView controllers? What about native libraries issuing network requests? To answer all these questions, we need to understand at which level this configuration is being enforced. Otherwise, we may fall into a false sense of security.
But how does it actually work? What component and at which level enforces these restrictions? Is it a kernel module such as SELinux or AppArmor? It is Linux containers? Is it the Android Runtime environment? Is it a compiler flag that acts at the time of Ahead-Of-Time or Just-in-time compilation to strip the insecure code parts?
In this article, we will try to understand how this flag works by example and how it can help us to make a more secure application. We will also learn by example which use-cases aren’t being protected by the flag.
Just before we begin, a short recap on why we need to protect all application communication with TLS.
Potential threat agents include:
Compromised communications can lead to a number of possible risks including:
To check how applications actually behave, we created an Android testing application and a python server for the application to communicate with. The test included trying different target APIs for the application and checking for each target API if the app could issue non-secure outgoing HTTP requests using different libraries as well as the default Android WebView controller.
For each library, three requests were sent: standard HTTPS, standard HTTP, and an HTTPS request to a URL which redirects the user agent to HTTP to simulate server-side-issued redirect attacks.
This section will go through the results for the different libraries tested.
OkHttp is the most popular Android HTTP library and is maintained by Square: https://square.github.io/okhttp/. For the test the latest 3.* and 4.* versions were used. The results for both versions were the same.
The following table shows the results of using our testing app with different target SDK configurations and without the clearTextTrafficPermited flag set (i.e. the default configuration) on multiple Android devices of different OS versions.
Android 5 API 21 | Android 6 API 23 | Android 7.1 API 25 | Android 8 API 26 | Android 9 API 28 | Android 10 API 29 | |
target API 21-27 | HTTP works | HTTP works | HTTP works | HTTP works | HTTP works | HTTP works |
target API 28+ | HTTP works | HTTP works | HTTP works | HTTP works | HTTP doesn’t work | HTTP doesn’t work |
The following table shows the results of using our testing app with different target SDK configurations and with the clearTextTrafficPermited flag set to “false” on multiple Android devices of different OS versions.
<base-config cleartextTrafficPermitted=“false”>
Android 5 API 21 | Android 6 API 23 | Android 7.1 API 25 | Android 8 API 26 | Android 9 API 28 | Android 10 API 29 | |
target API 21-27 | HTTP works | HTTP works | HTTP doesn’t work | HTTP doesn’t work | HTTP doesn’t work | HTTP doesn’t work |
target API 28+ | HTTP works | HTTP works | HTTP doesn’t work | HTTP doesn’t work | HTTP doesn’t work | HTTP doesn’t work |
The following table shows the results of using our testing app with different target SDK configurations and with the clearTextTrafficPermited flag set to “true” on multiple Android devices of different OS versions.
<base-config cleartextTrafficPermitted=“true”>
Android 5 API 21 | Android 6 API 23 | Android 7.1 API 25 | Android 8 API 26 | Android 9 API 28 | Android 10 API 29 | |
target API 21-27 | HTTP works | HTTP works | HTTP works | HTTP works | HTTP works | HTTP works |
target API 28+ | HTTP works | HTTP works | HTTP works | HTTP works | HTTP works | HTTP works |
When the network security policy is being correctly enforced, the OkHttp library won’t issue the request and will throw the following exception:
We can find multiple instances of checking the flag in OkHttp’s source code (https://github.com/square/okhttp) and can manually verify at which cases the check is being done. However, understanding the libraries internal logic may be required to identify the edge cases in which the flag could be ignored. The screenshot shows the source code for the relevant part of the connection chain:
This section shows results for async-http-client library but the results were the same for multiple other libraries that were tested. For the most part, libraries which use okhttp “behind the scenes” support the flag but other popular Android libraries which don’t use okhttp (like async-http-client and many others) don’t support the flag at all.
The following table shows the results of using our testing app with different target SDK configurations with the clearTextTrafficPermited flag set to “false” on multiple Android devices of different OS versions. Since the flag is unsupported, the behavior is in fact the same as if the flag wasn’t set or if the flag was set to “true”.
<base-config cleartextTrafficPermitted=“false”>
Android 5 API 21 | Android 6 API 23 | Android 7.1 API 25 | Android 8 API 26 | Android 9 API 28 | Android 10 API 29 | |
target API 21-27 | HTTP works | HTTP works | HTTP works | HTTP works | HTTP works | HTTP works |
target API 28+ | HTTP works | HTTP works | HTTP works | HTTP works | HTTP works | HTTP works |
In this section we tested the default Android WebView . This test showed that in fact the WebView controller behaves differently to the OkHttp library in the context of enforcing the configuration flag. In particular:
The following table shows the results of using WebView in our testing app with different target SDK configurations and without a value for the clearTextTrafficPermited flag set (default configuration) on multiple Android devices of different OS versions.
Android 5 API 21 | Android 6 API 23 | Android 7.1 API 25 | 8 API 26 | 9 API 28 | 10 API 29 | |
target API 23-27 | HTTP works | HTTP works | HTTP works | HTTP works | HTTP works | HTTP works |
target API 28+ | HTTP works | HTTP works | HTTP works | HTTP works | HTTP doesn’t work | HTTP doesn’t work |
The following table shows the results of using WebView in our testing app with different target SDK configurations and with the clearTextTrafficPermited flag set to “false” on multiple Android devices of different OS versions.
Android 5 API 21 | Android 6 API 23 | Android 7.1 API 25 | 8 API 26 | 9 API 28 | 10 API 29 | |
target API 23-25 | HTTP works | HTTP works | HTTP works | HTTP works | HTTP works | HTTP works |
target API 26-27 | HTTP works | HTTP works | HTTP doesn’t work | HTTP doesn’t work | HTTP doesn’t work | HTTP doesn’t work |
target API 28+ | HTTP works | HTTP works | HTTP doesn’t work | HTTP doesn’t work | HTTP doesn’t work | HTTP doesn’t work |
When the network policy is being correctly enforced, the WebView will show the following image
In this post, we won’t cover all configuration options, but only the main configuration options that should be understood. Full information can be found at https://developer.android.com/training/articles/security-config
To configure the clearTextTrafficPermited flag, we need to set the network security config at the manifest file level:
<application
android:networkSecurityConfig=“@xml/network_security_config”>
We then need to populate the network_security_config.xml file with the relevant settings.
We have included below some examples of the network config file (network_security_config.xml) and the actual meaning of the settings:
<?xml version=“1.0” encoding=“utf-8”?>
<network-security-config>
<base-config cleartextTrafficPermitted=“true”>
</base-config>
</network-security-config>
<?xml version=“1.0” encoding=“utf-8”?>
<network-security-config>
<base-config cleartextTrafficPermitted=“false”>
</base-config>
<domain-config cleartextTrafficPermitted=“true”>
<domain includeSubdomains=“true”>appdomain.com</domain>
</domain-config>
</network-security-config>
<?xml version=“1.0” encoding=“utf-8” ?>
<network-security-config>
<domain-config cleartextTrafficPermitted=“false”>
<domain includeSubdomains=“true”>appdomain.com</domain>
</domain-config>
</network-security-config>
<?xml version=“1.0” encoding=“utf-8”?>
<network-security-config>
<base-config cleartextTrafficPermitted=“true”>
</base-config>
<domain-config cleartextTrafficPermitted=“false”>
<domain includeSubdomains=“true”>appdomain.com</domain>
</domain-config>
</network-security-config>
In this section we will summarize some of the key questions which we have answered so far in our research:
Multiple tests show that manifest flag clearTextTrafficPermited is not being universally enforced by either the Android OS or at the SDK level. IN practice, each client-side library must implement support for the flag on its own and the actual implementation may heavily differ from one library to another.
Certain libraries such as OkHttp (or Volley which uses OkHttp behind the scenes) have full support for this flag according to their documentation. However, many other HTTP libraries which do not use OkHttp do not support it at all and the presence of the flag has no impact on the app’s behavior.
The Android WebView component has support for the flag but actual results show inconsistencies between the WebView and OkHttp implementations. In fact, Android WebView puts more clients at risk compared to OkHttp (refer to the WebView result table) as it does not enforce the flag unless target API is 26+.
Regarding other clients, such as binary communication libraries, etc, do not assume that these libraries will respect the flag. Instead, the source code of each such library should be checked for the code part that enforces the restrictions on outgoing insecure communications. The default assumption for all client-side network libraries should be that they don’t support the flag unless the library explicitly states that it does.
Despite the lack of actual system-wide level protection, the flag still offers a greater level of security in situations where it is supported by the client library and either the application’s target SDK is set to 28+ or the application is running on Android 7.1 or higher.