Securing Network Data Tutorial for Android
- Getting Started
- Understanding HTTPS
- Using Perfect Forward Secrecy
- Enforcing TLS With Network Security Configuration
- Understanding Certificate and Public Key Pinning
- Implementing Certificate Pinning
- Implementing TrustKit
- Using Certificate Transparency
- Stopping Information Leaks With OCSP Stapling
- Understanding Authentication
- Authenticating With Public-Key Cryptography
- Verifying Integrity With Elliptic-Curve Cryptography
- Verifying a Signature
- Signing a Request
- Additional Security Considerations
- Where to Go From Here?
Security is an important part of development. With more and more people turning to apps to work, users expect you to protect their data from prying eyes. Almost every app communicates over a network. You can keep your user’s information private by ensuring that your app is securing network data in transit.
In this tutorial, you’ll secure a simple Android app named PetMed for veterinary clinics that exchange medical information over a network.
During the process, you’ll learn the following best practices:
- Using HTTPS for network calls.
- Trusting a connection with certificate pinning.
- Verifying the integrity of transmitted data.
Download the project materials for this tutorial using the Download Materials button at the top or bottom of this page, then unzip them. Open the starter project in Android Studio 3.6.2 or higher and navigate to PetRequester.kt.
retrievePets() makes a simple call to retrieve JSON data for a list of pets and their medical data.
Build and run the project on an emulator or device with a network connection to see what you’re working with. Browse through the selection of pets. You’ll find that tapping an entry reveals a detailed view of medical data.
Everything looks fine on the surface, but your job is to ensure this medical data is secure.
URLs that start with http:// transmit data unprotected for anyone to view. Many popular tools are available to monitor HTTP traffic.
Because Pomeranians tend to be fussy about their privacy, the requests in this app use HTTPS. See for yourself by looking at the first line of
HTTPS uses Transport Layer Security (TLS) to encrypt network data in transit, an important layer of protection.
All you need to do to ensure a request uses TLS is to append “s” to the “http” section of a URL. That makes it very difficult to use those previously-mentioned tools to monitor the data.
However, this doesn’t provide perfect protection. You’ll want to take some additional steps to ensure your users’ privacy.
Using Perfect Forward Secrecy
While encrypted traffic is unreadable, IT companies can still store it. If attackers compromise the key encrypting your traffic, they can use it to read all the previously-stored traffic.
To prevent this vulnerability, Perfect Forward Secrecy (PFS) generates a unique session key for each communication session. If an attacker compromises the key for a specific session, it won’t affect data from other sessions.
Android 5.0+ implements PFS by default. It prohibits TLS ciphers that don’t support PFS.
As of Android N, you can enforce this by using Network Security Configuration. You’ll add this security to your app in the next section.
Enforcing TLS With Network Security Configuration
To enforce TLS on Android N and higher, right-click on app/res and select New ▸ Directory. Name it xml.
Then right-click on the newly-created directory and select New ▸ File. Name it network_security_config.xml.
In the newly-created file, add the following code:
<?xml version="1.0" encoding="utf-8"?> <network-security-config> <domain-config cleartextTrafficPermitted="false"> <domain includeSubdomains="true">github.io</domain> </domain-config> </network-security-config>
Here, you set
false, which blocks any network requests that do not use TLS, for specific domains that you specify.
You then added
github.io as a domain and set its
includeSubdomains attribute to
true. This will enforce TLS be required for subdomains like
Next, you need to tell the Android system to use this file.
In AndroidManifest.xml, replace the beginning
<application tag with this line:
To test that it worked, replace the first statement of
retrievePets in PetRequester.kt with this:
val connection = URL("http://kolinsturt.github.io/posts.json").openConnection() as HttpURLConnection
Here, you changed the URL to use HTTP and HttpsURLConnection to HttpURLConnection in order to test what happens when you send data without encryption.
Build and debug the project again in an emulator or device running Android N or newer. You'll see an error message in Debug that says java.io.IOException: Cleartext HTTP traffic to kolinstuart.github.io not permitted:
That's because Android blocked the calls so it won't retrieve unencrypted data. Your app will either crash or look like this:
Undo that change so that the code is back to this:
val connection = URL("https://kolinsturt.github.io/posts.json").openConnection() as HttpsURLConnection
Build and run the app. The app displays the data again, but this time, you know it enforced TLS.
That was easy, but there are a few more simple changes you can do to make your app even more secure.
For more information about this procedure, see the Update Your Security Provider page.
Understanding Certificate and Public Key Pinning
Now that you've taken the first step in securing the data, take a moment to consider how HTTPS works.
When you start an HTTPS connection, the server presents a certificate that verifies it's the real entity. This is possible because a trusted certificate authority (CA) signed the certificate.
An intermediate authority might also have signed an intermediate certificate — there can be more than one signature. The connection is secure as long as a root certificate authority that Android trusts signed the first certificate.
The Android system evaluates that certificate chain. If a certificate isn't valid, it closes the connection.
That sounds good, but it's far from foolproof. Many weaknesses exist that can make Android trust an attacker's certificate instead of one that's legitimately signed.
For example, a company might have a work device configured to accept its own certificate. Or a hacker can manually instruct Android to accept their installed certificate. This is called a man-in-the-middle attack — it allows the entity in possession of the certificate to decrypt, read and modify the traffic.
Certificate pinning comes to the rescue by preventing connections when these scenarios occur. It works by checking the server's certificate against a copy of the expected certificate.