Secure your Android App
Updated: May 10, 2020
The digital security arms race is on the go, and companies around the world invest a significant portion of their budget to secure their products and platforms.
Somehow there is a misconception that the Android platform is secured, but this is far from being true, and in many cases, security holes exist on production code even for leading companies in the market.
Android developers must be aware of the security holes they leave around in their code, and to take measures to ensure that they drop to zero.
1. Secure your API Keys
It is very common to use API Keys for services like google or other 3rd parties like analytics, ads platforms, etc.
Usually, you need to pass these SDKs your API KEY either from the Manifest or on runtime. for example:
This is from official docs of Adjust SDK - a developer can easily paste the App token as a hardcoded String, or store it in his strings.xml file. If he does it - it will be very easy for attackers that have this app installed on their rooted device to simply get their hands on this app token.
In order to prevent it - you need to consider using your Global gradle.properties and store this token there - the global gradle.properties file is a file that is not part of the project and can never be exposed to the outside world or leak to your Git repositories, this makes it an ideal place to put all your sensitive keys and tokens.
2. Secure your Shared Preferences The Shared Preferences is very common in day to day Android development. Developers store simple data like Strings and Booleans that they want to persist between app sessions. First of all, always use Context.MODE_PRIVATE when defining shared preferences.
The shared preferences is an XML file that is part of the app file system and can be accessed from every rooted device that has this app installed. One should be very careful with what he is storing there. You should always encrypt the values of sensitive data you store there.
Common encryption approaches such as Base-64 or SHA will not do - an attacker can easily decrypt them. This is where Android KeyStore kicks in.
The keystore encrypts the value with a secured and unique seed that - that makes it impossible for attackers to decrypt your values (Nothing is impossible but still will make them give up easily)
Unfortunately, it is not easy to implement and require a deep understanding in Encryption approaches, It took me some time to control that beast and make it stable in production environment, but it's worth it. Another important aspect of the Shared Preferences that developers are not always aware of is that Android auto backup feature will upload your shared preferences to the user's Google Drive to use it when the user re-install your app or migrate to a new device. You need to consider to exclude your shared preferences by using a backup rules XML file in your manifest file:
3. Protect your local Data Base
Most of the apps mange a local DB.
The problem is that similar to the Shared Preferences, Attackers can easily access the DB and extract the data from it - it becomes even more problematic when you store sensitive user information for example user location. The solution is similar to the shared preferences, we first need to encrypt the data and second, we need to make sure that the Auto backup feature does not upload our DB to the user's cloud. Encrypting a DB will require a third party - I usually use Sqlite3 Room DB as an abstracted layer can work with Sqlite3 flawlessly.
To exclude the DB from the auto backup you need to follow the same rules:
4. Restrict your components
Android components(BroadcastReceivers, Services, Content Providers, Activities) that are declared in the manifest.xml file, can be invoked by other apps. This, of course, is a weakness. This is where the "exported" flag and "permission" kicks in. You have the ability to control who can invoke these components. setting the exported flag to false in the manifest makes it impossible for other apps to invoke them.
However if you must allow other apps to invoke a component, you can make use of Permissions to gain more control over who and how they can access your component.
If you are exposing data to other apps through content providers make sure to define grantUriPemission in the manifest file in order to district other apps data from other apps to specific one-time operation, for example, ReadPermission will make it impossible for other apps to write data using your exposed content provider. You should consider using LocalBroadcastManager if you are planning to use broacast receivers internally in your app, Local Broadcast Receivers are more secure and their Intents cannot be intercepted by a 3rd party, Google mentioned in their docs that it is also more efficient than the traditional BroadCastReceiver.
5. Defend yourself from SQL Injections
In short, SQL injection is an attack that exploits raw SQL commands in order to read or manipulate data from your DB. an example of the risk of such an attack
Whenever you use a raw query in your code, you should stop and think twice, can someone from the outside can exploit this query. I Advise never to use raw-queries, instead of for example you can use a NON-Exported ContentProvider to access your own DB and using ContentResolver to execute your queries using the query method of the ContentResolver, this way you eliminate the risk of SQL Injections.
6. Encrypt your files
Whenever you store a file using Android, ask yourself, is this file contain sensitive data? if the answer is yes, then use EncryptedFile instead of a regular File, even if you store your files in the private app storage.
7. Handle WebViews with care
8. Use up-to-date Google libraries
Google consistently updating their dependencies(Google play services, FireBase, etc.) These updates are often addressing security weaknesses. It is extremely important to use the latest tools and push updates higher in the priority list in order to make sure our product is secured.
9. Use Google security checklist
Follow Google Security Checklist and make sure that nothing is going under your radar.
10. SSL Pinning SSL Pinning addresses the Man-In-the-Middle-Attack The Client should have an SSL Pinning certificate that matches the SSL Certificate on the backend. This will eliminate the risk of a middle man pretending to be your backend and stealing your data. The configuration is pretty easy and described well in the Following Article 11. Refresh Token Mechanism When a user authenticates with a backend a common practice is to generate a token that the client should pass for each request so the backend can identify the user and allow him to access sensitive data. The problem arises when this token has no expiration - in this case if the token leaks to an attacker he can access your backend. To avoid this - the backend should expire tokens it passes to the client and the client should verify if the token is valid before making a request - and if not- he can ask for the user to re-authenticate - and gain a valid token. The Industry standard for Rehresh tokens is JWT Token The backend generate a JWT token that the client gets and can decrypt it and know the expiration time, the backend will only accept the same JWT token that has not been manipulated in any way. This way you can better secure your client-server communication.
It is in our hands, and it is our duty to keep the security of our product our top concern.
We should invest more resources in security then we invest today, and fortify our product, making sure that we are not an easy prey