Android and keeping the data safer

Santi Aguilera
6 min readJul 2, 2017

--

When developing an Android application, eventually we get to the point where we have to store some data in the phone which we dont want to have exposed.

Having this values exploited would be disastrous, so how should we store them?

The aim of this post is to show weaknesses in the usual ways to store data and propose a better approach. This post refers specifically to the data that has to be stored in the phone. If you can isolate that data from the phone (since its a foreign environment where we dont have complete control) to a controlled environment (Eg: A backend), you should ALWAYS do it. For example:

Lets say we have a specific credit card where we want to trigger payments from the app, and you have a backend with the “payments api”, the credit card must not be stored in the phone. You should have it inside your backend completely isolated and encrypted, and the backend itself will use the resource when it needs to perform a payment.

A great quote to keep in mind that there is no silver bullet:

Absolute security does not exist. Security is a set of measures, being piled up and combined, trying to slow down the inevitable.

Diving in

Currently, some of the most seen ways to handle the storage of data are:

  • Keeping them in a Constant class:
public static final class Constants {
public static final String MY_TOKEN = "???";
}
  • Keeping them in a resource filestrings.xml or alike:
<resources>
<string name="my_token">???</string>
</resources>

These most usual ways are terribly awful, just by simply using an APK analizer and doing a grep we can find them! Definetily not what we want.

  • Adding proguard and storing them in a class (since proguard doesnt encrypt resources)

This gets far better, but the developer needs to proguard his whole application delicately (which is not something done in an hour).

We can still reverse engineer it and debug the APK checking for known places of initialization or high entropy (one example is the Application class declared in the manifest)

  • Storing them inside a native library.

Better than proguard + constants! Still, with a simple command like strings libwithconstants.so and some hours filtering all the symbols we dont want, we will obtain it. Also, they can still debug! So by debugging the output of your JNI method that returns the value, they will obtain it :(

  • Using SharedPreferences / AccountManager / etceteras.

This is useless, since you need to previously declare the constant somewhere to store it. Just by looking at how you obtain it, we grep the classes for the places where you store that key and we found it.

  • Keeping encrypted values and decrypting them before using them

This will make it harder since the attacker needs to know how you decrypt it.. But remember that its there in the code! He will need some more hours to find it but its still easily done.

Drafting a solution

There are some more ways, but with this we are able to address the main problems:

  1. The attackers are free to inspect our code and they have the tools to do it in a mega easy way
  2. They can debug the application
  3. If we perform some debugging checks or whatever, they can remove them and repackage another APK :(

Now, if combining most of the stuff we have seen in the right way, we can turn the tables and transform that “hour to find the super secret value I stored” into “A lot of time to find the super secret value I stored”. But, how?

  1. JNI is your friend. Although they can dump the symbols, having assembly code (Also note that depending on the ABI the assembler is different), imposes a far more secure layer than java bytecode (or far worst, some script that can decompile bytecode to plain java)
  2. Encrypting values is a must. We dont want them to dump the symbols of our JNI and find those keys. Let them find encrypted garbage, but not my key!
  3. We can split values into different places and join them with code (also in different places). Remember that finding symbols is a cake, but reading an unknown assembly code isnt.
  4. Perform checks about the phone from inside the shared object. For example: Check that we are not running adb, check that the phone isnt in debug mode, check that the app signature matches the one we have signed, check that the apk was intalled from certificated places, check the phone isnt rooted, that the phone is not an emulator, etc)
All this stuff together

With this, we have come to a point where the possibilities to find our keys have reduced to:

  1. The JNI way: Getting the assembly code from the shared object and understanding it (finding all the encrypted values and the decryption system).
  2. The Java way: Simulating an environment (stubbing classes) with all the checks we perform (that means, that the certificate matches ours, that it was installed from the same place, that its not in debug, not an emulator, not using adb, non rooted phone, etc) and debugging the library for finding it.

All together

With the problem and the solution in the table, I present you SecureKeys. This is a library that addresses the problems mentioned and proposes this same solution, but making its usage as easier as possible.

You can find it here: https://github.com/saantiaguilera/android-api-SecureKeys

How does it work?

You already know it :). For storing the values and configuring it, we use annotations, since they are not shipped to the APK and we can process them and create the native code for the libraries with their information.

Lets start by configuring our library:

// This goes in the root build.gradle
buildscript {
repositories {
classpath "com.saantiaguilera.securekeys:plugin:2.0.0"
}
}
// This goes in the module build.gradleapply plugin: 'saantiaguilera.securekeys'

dependencies {
compile "com.saantiaguilera.securekeys:core:2.0.0"
annotationProcessor "com.saantiaguilera.securekeys:processor:2.0.0"
}

And in our MainApplication we configure what we desire:

@SecureConfigurations(
aesKey = { /* 32 byte array length key */ },
aesInitialVector = { /* 16 byte array length iv */ },
useAesRandomly = false, /* Generates random key/iv for each build. This overwrites custom keys/ivs */
blockIfDebugging = false, /* Library shuts down itself if detects a phone in debug */
blockIfADB = false, /* Library shuts down itself if ADB is detected */
blockIfEmulator = false, /* Library shuts down itself if phone is an emulator */
blockIfPhoneNotSecure = false, /* ... if phone is rooted or alike */
allowedInstallers = { /* Array of strings */ },
certificateSignature = "hash" /* hashCode of the certificate used to sign the apk */
)
public class MainApplication extends Application {
public void onCreate() {
super.onCreate();
SecureEnvironment.initialize(this);
}
}

Finally, lets declare wherever we like our constants. Keep the strings inside the annotations! Please dont put them inside a Java object else we are back to the start!!

@SecureKey(key = "token", value = "???")
public class RestClientBuilder {
// ...
}

Or if you have a lot, use an array:

@SecureKeys(
@SecureKey(key = "x", value = "y"),
@SecureKey(key = "?", value = "test")
)
private int someMethod() {
// ...
}

Thats it! For retrieving them simply use one of the following:

SecureEnvironment.getString("token");
SecureEnvironment.getDouble("ads_poll_time");
SecureEnvironment.getLong("facebook_app_id");

Pretty easy right? Believe it or not, all what we have proposed is done without even knowing it.

Hope its useful for someone :) If you have any question, idea, opinion, critique, etc please dont hesitate to tell me!

--

--