Developing Secure Android Apps.

Ahmed Abdelmeged
ProAndroidDev
Published in
14 min readDec 22, 2018

--

Image from The Verge

I’ve been developing android apps for over 3 years now and I’m also a security enthusiast. During this journey, most of the new talks and blogs in the Android community were talking about the new shiny app architecture, latest libraries, trends in Android 🚀, etc. But I didn’t see anyone talking about security on mobile 🤐 very few folks talk about it. Actually, most users don’t care about your MV.. whatever architecture or new dependency injection framework you introduced to your app as much as they care about their app data and personal info to be secure and not to be compromised 😬. After all, when we finish our jobs we are also users and we want the apps we use on a daily basis to be secure 🙏. So I decided to make some researches in this area. I spent the last couple of months downloading random apps from google play with high downloads number and in the charts section to try to identify common security issues for researching purposes and make this article possible to help developers improve their security habits when developing to ensure maximum security for you and for the user.

I saw many bad practices that lead to many security vulnerabilities in these apps 😱. I started a thread about that on twitter you can find it here but it’s in Arabic so I will summarize it in my points and give a more comprehensive view about this topic 👨‍💻.

This article will be quite long so grab a cup of coffee and let’s dive into these 16 tips with detailed resources that will increase your app security 🤓. Note I’m not a security expert by any meaning I will just provide my point of view on this topic so If I had said something wrong please correct me to change it🤙

Let’s hack it!

Gif from Giphy

1- Never ship a production app without enabling Proguard/R8

You may be wondering why I put this as the first point and there are more important things to do to secure your app. And you are right but this, in my opinion, is the most important one because it will obfuscate your code and will make it unreadable so if someone tries to reverse engineer your app with some simple tools that anyone with little experience can do to access your code and see it crystal clear as if you gave it to him on a golden plate. Code obfuscation has many benefits first of all even if your app has some security vulnerabilities it will be very hard for hackers to reach them, will also prevent some people from stealing your code, recompiling it and publishing it in the store again with some ads and make a profit from your hard work. To enable it’s pretty easy in your build.gradle file.

buildTypes {
release {
minifyEnabled true
shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}

This will also make your APK smaller since it will remove unused code from your APK.

Gif from Tenor

But let’s be real here it’s not just these three lines to make your app work with Proguard/R8 😿 you need to modify some of your code to play nice with code obfuscation and it’s really a pain in the ass to debug Proguard errors and crashes and the build will take longer time. But the benefits of it are huge this awesome blog post will show you a real-world example to avoid these kinds of problems. If you want to be even more secure and take your security to the next level you can use a paid obfustication service like DexGuard.

2- APIs must have keys and store them safely

Most of the apps nowadays interact with some API to grab or post some data and many of them have an API key so you can’t directly call the API endpoints without this key. Unfortunately, some apps from my sample don’t have these Keys. I know it’s not an android related problem but it will affect your users and your servers severely if a hacker finds out about them. In some apps, I was able to make some requests into the API and some of them are user related endpoints. Someone can write a small script and DDOS attack your servers or steal your users’ data and both of them will ruin your business and your integrity in front of the users. So please add a key to your API and bonus point add a user token in the request header so if you see a malicious activity from one user you can immediately block it without affecting other users. I’m not an expert in this area maybe Backend developers can help us here since it’s their work🧐. Now we have an awesome API key. How to store in the code to make it extremely hard to access it? Native code yeah 🎉. Personally, I don’t ship any application with an API key store in Java/Kotlin code because they are pretty easy to access even with Proguard enabled. So you can store it in a C/C++ file using NDK. I know when some people here the word NDK and native code they get afraid and I agree with you NDK is hard but doing this simple task of storing some strings there it’s pretty simple. You will store your API keys and other sensitive static data there and you will call it from Kotlin/Java code.

Here is a blog post to help you make that and another one here. They are quite dated so maybe I will write another article on how to do it. I didn’t do it here because this article is long enough 😪.

Last thing here never ever put a server secret key in the client code 👎

Gif from Tenor

Example of that when you are dealing with a payment API like Stripe they give you two keys a client key you can use in android(Store it in native code)and server key for payment transactions normally this key will live in your server but some folks want to take a short path to deliver the product faster so they put it in the client. Never do this because if some hacker finds it you are pretty much F*cked up.

3- You must write Firebase security rules

We all love Firebase ❤️ It’s one of the best things that happened in the tech space lately. Many apps nowadays use firebase real-time database or Firestore to store some data in it and that is perfectly fine. The problem here is that developers don’t write the database security rules for that database and that is similar to you inviting a thief to come and steal your home while you are watching something on Netflix.

Gif from Giphy

When you build your app firebase SDK insert the database URL in your strings file like this

<string name="firebase_database_url">https://myawesomeapp-123456.firebaseio.com</string>

It’s fine because it’s like the API URL that you put in your java code and you will need an API key to restrict access to it. The key here is the security rules. When they see this he/she will say firebase is not secure and start to swear 😤🤬 and maybe leave the service. But before you start doing this did you write the database or storage security rules? The answer will probably be no. Actually, with simple curl command, anyone can delete and manipulate your entire database. I get it writing the rules is hard and not a funny thing at all and you are in a hurry to release and show your great idea to the world but at the same time, a small mistake can ruin your app and your business. So please before you release your app make sure to write it for the database and storage. The firebase documentation is a great way to start and this video. After that, you can put your database URL in your Twitter bio and it will be secure 🤓.

4- Restrict google API key access

Most of us use Google services in our apps. A common example is using Google maps services. Google provides you with an API key to access its services and it’s often stored in strings.xml. That’s fine Google security model is pretty secure if you use the right way. Many developers go to the Google developer console create a key and hurry up to add it to their apps.

Gif from Tenor

By doing that you won’t hurt your users But it will hurt you badly. If a bad person finds your unrestricted API key with 5 lines script he can make a lot of requests using your Key and that will eat up your quota and will make you receive an invoice as tall as Mount Everest. The solution is pretty simple just restrict your API key to your app from Google console.

Don’t just add your package name because anyone can duplicate it. But also add your SHA-1 certificate fingerprint from your release key. That will make it pretty much impossible for others to make a request using your API key outside your app. Also, restrict the number of APIs that key can access it.

5- HTTPS everything

Android apps normally interact with the network to grab some data from the internet. When you do that use HTTPS communications to ensure maximum security and the data you get from the network or post to your servers won’t be compromised. Android P by default crashes any apps that use HTTP so make sure all your communications are safe. Many Android users connect to several open Wi-Fi hotspots in public areas every day. Some of those hotspots could be malicious. A malicious hotspot can easily alter the contents of HTTP traffic to make your app behave in an unexpected manner, or worse still, inject ads or exploits into it. If you have some domain that you really want to use clear text allows only this domain to make HTTP request you can learn more about this here.

6- Never ever store Password and private keys in shared preference.

Ok, this point specifically makes me so angry when I see any developer at any platform storing user passwords or private keys on the client especially on shared preference. Making things worse they store it in plain text 😡. Why guys?

Gif from Giphy

This is like putting the user password as the background of your app. Any script kiddy can get the user password and steal their account and the user will sue you after that. Use access token instead(Encypt it on desk)and the user can revoke it at any time when his phone gets compromised. If you can’t handle user auth and credentials use service like Firebase Auth will make your life much easier and make you and your users safe.

7- Watch what you print in LogCat

All of us use LogCat to print logs in the app for debugging purposes. You may print your service URLs, response body, usernames, crashes, etc. To debug your code and that’s fine for a debug build. Many developers forget to remove these logs statement when they make the release APK. Everyone can see these logs on their computer and this logs could contain sensitive data that if it’s misused, it would be a nightmare for you.

Gif from Giphy

I know to go all over your code to check and remove the log statement is a tedious task. So to avoid all of that you can use a library like Timber by Jake Wharton. That will make you enable or disable logs in a centralized place. Here is an example. You configure Timber in Your Application class

class App :Application(){
override fun onCreate() {
super.onCreate()
if(BuildConfig.DEBUG){
Timber.plant(Timber.DebugTree())
}
//Reset of your code
}
}

And log anywhere in your app like this.

Timber.e("I'm an error log")
Timber.i("I'm an info log")
....

You can check this article for advanced logging with Timber.

8- Use Internal Storage for Sensitive data

Store all private user data within the device’s internal storage, which is sandboxed per app. This means that files cannot be accessed by any other app on the device. Your app doesn’t need to request permission to view these files. Files inside this directory are very secure because they use the MODE_PRIVATE file creation mode by default. As an added security measure, when the user uninstalls an app, the device deletes all files that the app saved within internal storage.

You can access it like this. A bonus point encrypt that data before storing it on the desk

val file = File(context.filesDir,"my_super_secret_file.txt")

9- Do not pass sensitive information through Broadcast

Some developers use Broadcast to make some communication between different components of the app like communicating between background service and an Activity. That’s ok if you are using it to update the UI. But if you use it to process user private data never use a normal Broadcast because other apps can register it and listen to your events. A better solution is to use LocalBroadcastManager for broadcast data within process/app. This Github Gist shows you how to use it to communicate between a service and an Activity. You can check this article for more details.

10- Use WebView carefully

Many apps use Webview to load some HTML content or load some web pages. WebView can introduce many security issues in your application if it’s misused. Because WebView consumes web content any common web security issue like cross-site-scripting, etc can affect your app. In addition, you should never enable JavaScript interface support unless you completely control and trust the content in your app’s WebView. Check the Android documentation to see how to secure your WebView.

11- Keep your dependencies up to date

All of us use third party libraries to achieve common tasks in android like networking, loading images from the network, database, etc. Most of these libraries are updated on a regular basis to improve performance, security and add new features. So always update all your libraries to gain these benefits because some versions of the libraries may introduce you a security vulnerability and they fix it in the next version. Of course, you don’t want your app to be infected with security issues 😁.

Gif from Giphy

Note here when you add a library to your app, It basically has the same access as your app code. So be very careful which libraries you add to your project because if you add a bad one all of the other security tips will be useless. Make sure to add only libraries from known developers or If it’s open source check it’s code before adding it to your app.

12- Protect your Service and content provider with Permission

You are probably using a background service to do any kind of task, for example, downloading something, Process payment, etc also you don’t want other apps to access this service.

Gif from Tenor

So to prevent that make sure to set the service exported flag to false. Like this

<service
android:name=".MyAwesomeService"
android:exported="false" />

You probably also use a content provider to load the data in your app. It’s not the case that every time we develop ContentProvider for data exchange between applications but ContentProvider can be developed for a single application or private. To prevent leaking your data make sure to also set the exported flag to false like this.

<provider
android:name="ContentProvider"
android:authorities="com.ahmedabdelmeged.contentprovider"
android:exported="false" />

13- Encrypt data on External Storage and Check the validity of that data

You may need to store some data on External Storage since the internal storage capacity of an Android device is often limited. So you have no other choice. That’s fine most apps do that to store media files. Because data on external storage can be directly accessed by both users and other apps on the device, it is important that you store it in an encrypted format. One of the most popular encryption algorithms is AES, short for Advanced Encryption Standard, with a key size of 256 bits. Writing code to encrypt and decrypt your app’s data using the javax.cryptopackage, which is included in the Android SDK, can be confusing. Therefore, You can use third-party libraries, such as Facebook's Conceal library, which are usually much easier to work with.

You should Perform input validation when handling data from external storage as you would with data from an untrusted source. You should not store executables or class files on external storage prior to dynamic loading. If your app does retrieve executable files from external storage, the files should be signed and cryptographically verified prior to dynamic loading. You can read more details about this topic here.

14- Avoid Asking for Personal Data

User privacy is one of the most important things nowadays. We are in the time of GDPR and much other data privacy regulations. Users really care about their data and don’t want it to be exposed to anyone.

Gif from Giphy

So avoiding ask the user of his personal information will be much safer for you and will remove the headache of securing it on your side. Unless you have a good reason and a very secure infrastructure to collect, store, and transmit personal user information, you must avoid directly asking for it in your apps. You can use service like Firebase Auth to manage that for you.

15 -Don’t process any payments on a rooted device.

Many apps have an in-app payment or making critical tasks never do this kind of tasks on a rooted device. Automatically disable these features on a rooted device. Because the rooted device can change your code at runtime and alter the behavior of it.

You can detect rooted device using the code is this StackOverflow answer. For more details on that topic check this article.

Gif from Tenor

16- Use as little permissions as possible

During developing developers tend to put a lot of permissions in the Manifest file thinking that they will use it when they develop their app and after finishing the app they make the release build and forget to remove these permissions. Having a lot of permissions for one app is not secure at all and users don’t like to use apps with too many permissions unless they have a really good reason for it. Your app should request only the minimum number of permissions necessary to function properly. When possible, your app should relinquish some of these permissions when they’re no longer needed.

Conclusion

Following these tips, will ensure you a more secure app but that’s not all the things you can do to make your app secure 🕵️‍♂️. But these ones are the most common in most apps nowadays If you want more advanced security tips check this talk on Google IO 2018. After all, I’m convinced that there is no 100% secure app 🙄. If you have other important tips or opinions share it with us in the response or keep in touch with me. Happy coding!

Twitter:A_K_Abd_Elmeged

Github: Ahmed-Abdelmeged

--

--