Figure1

Many Android apps that interact with the cloud use a client – server architecture with the phone acting as a client and much of the heavy lifting taking place on the server. Sometimes you have control over the server, and sometimes it’s a third party who provides the data, e.g. traffic updates, stock market data, or weather information etc. Typically these third party providers use an API key as a simple authentication mechanism to grant access to these resources, and as a way to charge for their data.

Related: How to hide your files, phones and apps on your phone

Now there are lots of reasons why you might want to keep your API key safe. Obviously if you’re being charged for access to the API data then you don’t want someone to decompile your APK, find your API key, and then start using it in their own apps. It’s either going to cost you money for these stolen API calls, or worse still, create frustration for your existing users if your API suddenly stops working because the provider stops your access when it spots the increased activity. If your API key does become compromised then getting a new key out to your users is never easy. Do you revoke the old API key and switch to a new API key, potentially leaving users of earlier versions stranded? Or do you keep the old API key alive while you wait for the old users to upgrade, but run the risk of further bogus charges? Either way is not going to be 100% satisfactory, so it is best to avoid the situation completely.

Figure2

Before we look at the options for hiding your API key, let’s look at how someone can find your API key. There are two basic types of attack, firstly by decompiling the APK to see if it is stored somewhere in the code, and secondly using a Man in the Middle (MITM) attack to see if it’s transmitted insecurely  over the web. To access the network traffic the hacker needs to root their phone using something like ProxyDroid. This allows the traffic to be intercepted using the Charles Proxy tool or BurpSuite using a MITM attack.

API keys are often sent via a URL. For example, you can make a request to the Weather Underground API using the following URL,
http://api.wunderground.com/api/2ee858dd063ef50e/conditions/q/MI/Troy.json where 2ee858dd063ef50e is the API key. If you send the request via HTTP then the API key would be clearly visible to anyone doing a MITM attack. Sending it correctly via HTTPS will make it impossible to see the API key via proxying. Note I used the word “correctly”. The media is littered with examples of MITM attacks where the HTTPS was intercepted because the app accepted a fake SSL cert generated by tools like Charles Proxy rather than checking that it came from a reputable Certificate Authority (CA). We’ve also found examples where the device itself didn’t handle certs correctly and any HTTPS traffic was open to a MITM attack. You also need to be careful about where the API key lives before you assemble the HTTPS call. We’ll look at that next.

In my audits of hundreds of Android mobile apps I have seen many attempts to hide the API key. In this article we look at some of the ways developers have tried to protect your API key.

The possible options are as follows:

  1. In shared preferences, assets or resources folders
  2. Hardcoded in Java
  3. Using the NDK
  4. API key Public/private key exchange

In shared preferences, assets or resources folders

Below is an example of an API key that was found by unzipping the APK file after it was pulled off the phone and looking in the asssets folder.

<string name="mapsAPIKey">0M5WrSiVVUbMohPNDsrAmIb0hR6NftlV9E1hjvA</string>
<string name="mapsAPIKeyProd">0M5WrSiVVUbMohPNDsrAmIb0hR6NftlV9E1hjvA</string>
<string name="mapsAPIKeyDev">03JOvW5GGOOkIY0n70rIiPPFlZjqOqI3SFhKoYA</string>

The most common place I find that developers store API keys is in the shared preferences, below is a an example. It’s common practice to put APIkeys in the resources folder in the APK, which then gets stored in shared preferences after the app is first opened.

<map>
   <string name="photoUploadAppKey">IO42DUS7M4C2</string>
   <string name="photoUploadAppKeyStag">IO42DUS7M4C2</string>
</map>

Hardcoded in Java

Figure3

An API key is much much more likely to be found in the assets or resources folder than in the decompiled code because it’s much easier to update xml than compiled code. But it still happens regularly. Below an example of an API key found in a pharmacy app that was stored in a Constants.java file.

package com.riis.pharmacy;

public class Constants {
     public static final String API_TOKEN = "Nti4kWY-qRHTYq3dsbeip0P1tbGCzs2BAY163ManCAb";
}

Using the NDK

The Android Native Devlopment Kit (NDK) allows you to write code in C/C++, and can be very useful when you’re trying to hide things like API keys. The good news is that NDK libraries can’t be decompiled making the information harder to find. The NDK compiled code can still be opened with a hexidecimal editor but by the nature of the API key they are harder to pick out in a hexidecimal editor.

#include
#include

jstring Java _ com _ riis _ apindk _ MainActivity _ invokeNativeFunction(JNIEnv* env, jobject javaThis) {
    return (*env)->NewStringUTF(env, "Nti4kWY-qRHTYq3dsbeip0P1tbGCzs2BAY163ManCAb");
}

You should include a call to see if the checksum matches your APK as otherwise someone can call your NDK library outside of your app to recover the password. This approach is ultimately not going to be good enough to stop someone from reading the binary. But it is a better option to consider if you have no other choice than to put the API or encryption keys on the device, for example if the backend server belongs to a third party provider. Disassembled code also rapidly becomes more difficult to understand as it gets further away from these simple helloworld examples.

Public/private API key exchange

While HTTPS securely hides the API key during transmission, retrieving the API key from the phone so the HTTPS call can be made is a real problem for developers. As we’ve seen hiding an API key is not dissimilar to how people try to hide an symmetric encryption key (see my earlier article on where to store your passwords). All these keys can be recovered using the adb backup or adb pull command and a little perseverance. So even if the hacker can’t perform a Man in the Middle attack they can still see how the call is put together and the API that’s used in the call so they can hijack your resource if it’s useful to them. We could try try to encrypt the API key on the phone but if you’re storing the encryption key on the phone, then you’re simply adding one extra step to get to the API key.

However you can use a public/private API key exchange to safeguard the API key, so it can no longer be stolen. The downside to using public/private key exchange for passwords is that the phone can’t be in airplane mode when you login as the decryption takes place on a remote server. That doesn’t apply to API keys as they’re always using network communication.

The key should first be encrypted with the public key on a remote server using your favorite library such as Google’s Keyczar. You can either store it in the res/xml folder so you can retrieve it when someone opens the app on the phone and then store it in the app’s shared preferences, or send it to the app from the remote server so it can be reused when needed. When the URL call is made the encrypted API key is sent via HTTPS and then decrypted on the server so it can be compared to the real API key. You can also use some other dynamic piece of information such as the username to make sure the encypted API key can’t itself be used as an API key. There is nothing to stop someone else from sending the same encrypted API key from a different app to defeat your efforts. You can stop these replay attacks by encrypting your API key together with a one-time only randomly generated number or GUID, known as a nonce. The nonce and API key are sent to the server for API authentication, and the API key and a new nonce are sent back to the client to be saved in the shared preferences after each login for the next use. Every generated nonce is saved when created and marked as used once it has been sent from any client.

Conclusion

What option you choose is probably going to be determined by how much control you have over the backend server. If you don’t have any control then you’re probably going to have to hide the API key using the NDK. If you do then we recommend the Public/Private encryption of the API key using nonces to prevent any replay attacks. In the next article we’ll look at the security implications of supporting earlier Android OS versions, as well as how some Android phones are more secure than others.

Subscribe to our Android Developer Newsletter
Join our Android Developers newsletter to get all the top developer news, tips & links once a week in your inbox

About the author

Godfrey NolanGodfrey Nolan is the founder and president of the mobile and web development company RIIS LLC based in Troy, Michigan, and Belfast, Northern Ireland.He has had a healthy obsession with reverse engineering bytecode. See more from him here. He’s also the author of Bulletproof Android: Practical Advice for Building Secure Apps.

 

Show 8 comments