How to secure your Android apps

Neha Agarwal
9 min readSep 21, 2022

--

Image credit freepik.com

Nowadays mobile applications have become an integral part of any business and that’s the reason Mobile apps remain the prime target for malicious activities. Mobile app security is a big concern and that’s the reason organizations should secure their apps to avoid data theft and fraud. In this article, we will provide a checklist that any Android developer can follow to avoid such risks.

Source code protection

Securing an Android application starts with securing the source code.

Code obfuscation

You can use the Android Proguard Tool to obfuscate, shrink and optimize the code. Obfuscated code will be harder for people to reverse engineer. Progaurd renames the classes, fields, and methods with semantically obscure names and removes unused code.

More info is available here, on configuring Proguard and its usage.

Integrate tamper detection

Attackers can tamper with the app, re-sign it and publish the malicious version to third-party app marketplaces. There are a few ways that you can implement in the app to detect if a user is using the genuine app and is installed from a valid source.

  • Verify the installer: We can identify the installer of the application. If you distribute your application only on Google Play Store, then other installers will be invalid. The below code is checking if the app installer is valid.
  • Verify the app’s signing certificate: When any android application is published on the Google play store it has to be signed with a certificate. If anyone wants to tamper with the application they try to sign it back with a fake certificate as the attacker will not have the original certificate. The below code snippet will help to check if the app is signed with a valid certificate.
  • SafetyNet: The SafetyNet is a tool from Google to help developers to spot tampering attempts and take actions to avoid them (like not letting the user run the app).

Avoiding confidential info as part of the code repository

Any secret information such as API keys, encryption keys, etc shouldn’t be part of the repo/code base. Instead, they should be fetched from environment variables while building the app.

We can use BuildConfig to save the keys. Let’s understand the implementation for the same:

  • Create file keys.properties in your root directory with the different secret keys. The name of a file can be any name that suits your requirements.
  • To avoid these keys being available in your repository, make sure to exclude the file from being checked in by adding it to the .gitignore file
  • Add the below implementation to read from this file in your app/build.gradle file.

Now you can access the keys as below:

In this way, your secret keys won’t be stored in the code base and will be used for building your application.

Keep app dependencies up to date

Before deploying your app, make sure that all libraries, SDKs, and other dependencies are up to date:

  • For first-party dependencies, such as the Android SDK, use the updating tools found in Android Studio, such as the SDK Manager.
  • For third-party dependencies, check the websites of the libraries that your app uses, and install any available updates and security patches.

Be careful while using third-party libraries

Using Third-party libraries has always been a convenient way to reuse the code. It’s a great way to save a lot of time on such tasks which an already developed library can perform. With this great benefit, it also comes with a security overhead, as these libraries may inject some harmful code into our code base. We need to be very cautious while using any third-party library, one should always check the GitHub link, license, and code/security review before integrating them into the app.

Make sure the user is using the latest version of your app

On the launch of an application, we should always check if a user is having the latest version of our app against the server. This will make sure that the user is up to date with all the features and minor patches. If the user is not using the latest version, we should block the app usage and force the user to install the latest updates from Play Store.

Secure Client-server communication

SSL Pinning

SSL Pinning is a very useful mechanism to avoid Man in the Middle attacks. When a client connects to a server, the client checks if any SSL Certificate Authority trusts the received server’s SSL certificate. This makes sure the client only communicates to a designated server and does not respond to fake servers.

Different ways to implement SSL Pinning in Android are covered in this article in detail.

Use Token-based authentication

We should make sure to use token-based security to authenticate legitimate requests from your app to its backend.

For security reasons, most API access points require users to provide an authentication token that can be used to verify the identity of the user making the request. The client app usually fetches the token on successful login or registration then saves the token locally and provides the same to subsequent requests so that the server can authenticate the user.

Using Retrofit, we can easily achieve this by simply implementing an interceptor and providing the authentication token in every request.

Secure user-sensitive data

Screen Recording & Capturing

Sensitive information can be exposed from the app by screen recording or screenshots. This security check plays a vital role in banking applications where information such as account number, account balance, and transaction details can be compromised if a screenshot or screen recording is performed.

To disable screen capture in your app, set WindowManager.LayoutParams.FLAG_SECURE in onCreate() of your activity, before setContentView() and clear it in onDestroy() to enable it again for another activity.

Authenticate sensitive data access

Always authenticate the user before providing access to any sensitive information in the application. For authentication, we can use either password or biometric authentications to make sure only the valid user can access the information.

To know more about biometric authentication refer to this.

Rooted Device detection

Rooting is the process of unlocking an Android in order to access higher administrative privileged controls. We can think of rooting an Android device as promoting ourselves from a system user to a system administrator.

System security and safeguards can’t be guaranteed after the root. Within the rooted device, sensitive data is at risk, including gaining access to private information like contact lists, emails, and other data, or collecting data like credentials and passwords. With a rooted device, a user or malicious program can elevate their permissions to root and circumvent this protection giving them access to other apps’ private/sensitive data.

So to avoid data theft, it is the best way to check your application whether the device is rooted or not but there is no 100% best way to check for root.

  • Check for SuExists: Different file systems check for the su binary.
  • Using libraries: we can use the below libraries to check if the device is rooted or not:
  1. RootBeer
  2. SafetyNet API

Logs

  • Remove logs for an application in production: You mustn’t have any log statements for an application in production. To enable your log statement calls only during the development phase, Android offers you the BuildConfig.DEBUG property. This flag is set automatically to false when an application is deployed into an APK for production and then, it’s set to true during development.
  • Don’t include sensitive data in system logs such as passwords, account numbers, credit card numbers, etc.

Privilege management

  • Request a minimal number of permissions: When the user requests an action in your app, your app should request only the permissions needed to complete that particular action. Depending on how you are using the permissions, there might be an alternative way to fulfill your app’s use case without relying on access to sensitive/private information.
  • Associate runtime permissions with specific actions: Request permissions only when the user is going to use that particular use case. For example, if your app allows users to send a video message to others, wait until the user has navigated to the messaging screen and has pressed the Send video message button. After the user presses the button, your app can then only request access to the camera permission.
  • Consider your app’s dependencies: When you include a library in your app, you also inherit its permission requirements. Be aware of each dependency’s permissions and what those permissions are used for.
  • Be transparent: When you make a permissions request, be clear about what you are accessing, and why, so users can make informed decisions.

Secure local storage

Keystore for sensitive data

SharedPreferences is not a secure place to store sensitive/private data because the data is saved in simple key-value pairs in an XML file.

​​In some cases, it can easily be hijacked. As an example, if you saved some confidential information in SharedPreferences, on a rooted device it can easily be accessed through a regular file manager.

Android Keystore store passwords, or other sensitive data in it, encrypt the info, and decrypt that data right back. The Keystore is not only for passwords, but it can also be for any sensitive data, and it does so in a way where it is much more difficult for attackers, or unauthorized software to get this information from us.

The Android KeyStore system allows you to store cryptographic keys in a container, making them more difficult to extract from the device.

Once keys are within the key store, they can be used for cryptographic operations with the key material remaining non-exportable.

Moreover, it offers facilities to restrict when and how keys can be used, like requiring user authentication for key use or restricting keys to be used only in specific cryptographic modes.

More information on the implementation of Keystore can be found here.

Input Validation

Most mobile applications accept input from the user and if the user inputs are not validated correctly can exploit the application.

It is always suggested to prevent attacks as early as possible in the processing of the user’s request. Input validation can be used to detect malicious input before it is processed by the application.

  • If accepting any file as an input make sure we identify the required file formats and size and validate them once the user uploads any file to process.
  • Check the minimum and maximum text length
  • Set appropriate input type for EditText to accept only required type of data e.g. android:inputType=” phone”
  • Validation should enforce the correctness of their values. For example, the start date is before the end date, the price is within the expected range, etc.
  • Validation should enforce the correct syntax of structured fields. For example, SSN, date, currency symbol, etc.
  • Try to apply regular expressions for structured data covering the String and not allow any other string or number

Security audit tools

Now we see that automation is everywhere, and security is not an exception. It is very important to use strong security audit tools, and scan your application during the development cycle or at the end of the development of an application, to identify loopholes or vulnerabilities in the application.

There are many tools that we can use as per the application’s requirements such as QARK, Veracode, MobSF, etc

Conclusion

Application security is something that gets neglected in the rush of delivering features on time. But considering different attacks we should allocate some time while developing any application to retrospect how our application will survive such attacks. We have listed some of the best security practices that we can follow as a developer, to make sure user data and privacy is not compromised because of loopholes or vulnerabilities in the application.

Team Credits

Akshata Shelar | Ankush Yerawar

--

--