The picture shows conveys about protecting sensitive information

Hiding Sensitive Data in Android App

How often do we worry about our sensitive data shipped with the apk without security?

In this blog post, I am going to talk about hiding sensitive data in your Android app. I will explore this topic by answering the following questions-What, Why, and How?

The What?

What do we need to hide in our code? There are many examples of sensitive string data that need hiding-

  1. API keys from third-party libraries. Example- Google Maps API key
  2. URLs to make server API calls. Example- Base URLs to our private server
  3. Default login credentials (if your app allows a demo login to showcase various features before users can subscribe to them). Example- Username and password

The Why?

Why do we need to hide the data when Android already uses Proguard to protect the classes?

There are many ways in which you can store sensitive string constants in your Android application to use in web service calls to your server like:

  1. Store as string constants in some .kt/.java file
  2. Store as string resource in strings.xml
  3. Store inside Build.Config from the Android Gradle plugin
  4. Directly use the strings as literals in the code

However, if your application is reverse-engineered (decompiled), the string constants can be easily intercepted and misused.

Strings stored using the above mechanisms can be intercepted in a decompiled application like so-

For brevity, I would be taking a base URL for interacting with a server as an example throughout the post. Let’s consider following the literal as an example-https://domainname/functiontype/.

  1. The URL intercepted from a decompiled constants class-

2. The URL intercepted from decompiled strings.xml:

3. The URL intercepted from a decompiled BuildConfig class-

4. The URL intercepted from decompiled from the code using it as a string literal-

Thus, the common ways to store strings in Android applications fail to provide any secure way to store untraceable string data.

The How?

So, how do we secure the strings? One way to secure the string constants without exposing them to hackers in the decompiled version is by using Android NDK.

Using C/C++ JNI native code to hide sensitive string data with NDK and CMake

Note: This method does not guarantee full proof security against reverse engineering (Although the .so files are very hard to decompile, it is not impossible to do so) but it adds a layer of security.

The idea behind this way of securing sensitive data is to store the sensitive strings in C/C++ classes and fetch these strings by function calling from Java/Kotlin classes.

The code for C/C++ classes is stored inside generated .so files which are harder to decrypt than the Java/Kotlin classes from the decompiled version.

Getting started

  1. Install CMake from Android SDK

Go to Tools -> SDK Manager -> SDK Tools -> Check the CMake checkbox to install the latest CMake.

Install Latest CMake
Install Latest CMake

2. Install Android Native Development Toolkit (NDK)

You can tick NDK from the settings dialog above along with CMake to install NDK. Otherwise, you can download NDK from the official site. I already have NDK downloaded on my computer. I am therefore using the path to its folder in the local.properties file.

local.properties file to store path to android NDK
local.properties file to store path to android NDK

3. Create a folder “cpp” under app/src/main. Create a C/C++ file to store and access your base URL from- native-lib.cpp under cpp f`older.

native-lib.cpp
native-lib.cpp

The above is a simple C++ function to return the base URL from a method named stringFromJNI().

Things to note

For making the function available in Java/Kotlin code, the format of the function name is Java_package_name_activity_name_function_name

  • activity_name is the name of the activity class inside which this native function is to be used.
  • package_name is the name of the application’s package name where the activity resides.
  • function_name is the name of the function that will be called from Kotlin/Java classes to fetch the URL.

4. Create a CMake file named CMakeLists.txt (the file name has to be the same) to allow your app to build the native C++ code into a library.

The add_library The CMake command is used to instruct CMake to build a native library with the name specified (“native-lib”) from the native code from the path to the .cpp file specified (src/main/cpp/native-lib.cpp).

Along with creating CMakeLists.txt, add the following to the app’s build.gradle file to specify the path to CMakeLists.txt for CMake to compile and build the native code using NDK.

5. The next step is to make the native code available for use in Java/Kotlin activity. To enable this you must include the following in your Activity.

Note: For this example, I am using retrofit to illustrate the use of network calling.

Kotlin code-

Java code-

  • The loadLibrary() a function is called from the Activity’s init block of companion object (Kotlin)/static init block (Java) because in this example we are required to access the native code in the launcher activity’s onCreate() method.
  • Note that the access modifiers- native in Kotlin and external in Java are used respectively to be able to access functions from native.
  • Also, note that the external function in Kotlin is annotated with @JvmStatic to enable static access to it. (i.e. direct usage of ApiHelper.baseUrlFromJni())

6. Build and Run the project.

That’s it!

Try to decompile your built apk and check if you can find the URL defined anywhere in the decompiled version. All you can see is libnative-lib.so files in the lib directory which is very hard to decipher.

Categories