Securing Network Data Tutorial for Android

In this Android tutorial, you’ll learn how to keep your information private by securing network data in transit. By Kolin Stürt.

4.7 (7) · 1 Review

Download materials
Save for later
Share
Update note: Kolin Stürt updated this tutorial. He also wrote the original.

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.
Note: This tutorial assumes you’re already familiar with the basics of Android networking. If this subject is new to you, read our Android Networking Tutorial first.

Getting Started

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.

Right now, 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.

PetMed Details View

Everything looks fine on the surface, but your job is to ensure this medical data is secure.

Understanding HTTPS

URLs that start with http:// transmit data unprotected for anyone to view. Many popular tools are available to monitor HTTP traffic.

Some examples are Wireshark, mitmproxy and Charles.

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 retrievePets.

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.

Note: It’s also a good practice to limit the amount of data you send from your app to just the essentials.

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 cleartextTrafficPermitted to 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 kolinsturt.github.io.

Next, you need to tell the Android system to use this file.

In AndroidManifest.xml, replace the beginning <application tag with this line:

<application android:networkSecurityConfig="@xml/network_security_config"

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:

PetMed Cleartext Error

That's because Android blocked the calls so it won't retrieve unencrypted data. Your app will either crash or look like this:

App with no data

Sad face

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.

Note: Often, when security researchers find vulnerabilities in software, the software company releases a patch. It's a good idea to make sure you've patched the security provider for TLS. If you see an error such as SSL23_GET_SERVER_HELLO:sslv3 alert handshake failure during your debugging, this usually means you need to update the provider.

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.