So what are the five top tips for developing secure Android applications?
Android applications exist in an apk format. These apk files are similar to zip files; they just have a different extension. By renaming the apk to a zip file, you can access everything inside needed to run it. The apk can be easily pulled from a phone using a file manager or android debugging bridge (ADB) to transfer it directly to your computer.
These apk files can be reverse-engineered to Java source code. If the application was not obfuscated, this would allow easy access to the original algorithms, embedded URLs and passwords. The source code can then be modified and the application rebuilt – resulting in changes from circumventing licensing checks, changing the program flow to other more malicious actions.
Some of the protocols followed to keep information safe are:
- Obfuscating strings
- Obfuscating code
- Validate the apk
- Using HTTPS and SSLSocket
- Saving data with encryption
Obfuscate strings
One of the most obvious steps is obfuscating sensitive strings. Once someone has the de-compiled source code, it’s easy to trawl through the resulting Java to find important strings, which can include website URLs, default passwords, certificates and predefined communications protocols (good if you want to try injecting some of your own data).
Fig.1: Server address decompiled in plain text
To protect against this, different levels of obfuscation can be used, each with a higher level of complexity. Additionally, you can throw ‘dummy’ strings into your code with the idea of making it harder to determine what is relevant.
Simple solution: Store strings as arrays of bytes or encode the strings using Base64. Android has a utility method for this:
Fig.2: Android’s base 64 encode utility method
An advanced solution is to encrypt the strings. This can be achieved by using a secure encryption algorithm such as AES but you should store the encryption key appropriately; using the Android keystore is recommended. If your application has access to online features, you can also use these to check permissions and access to app features.
Fig.3 Decompiled server address after encryption
Obfuscate code
Code obfuscation – like string obfuscation – is about making the code harder to reverse engineer. Android build systems come with a tool called ProGuard, which when configured, will obfuscate code as well as optimising and shrinking it. It does this by renaming classes, methods and fields to identifiers with no semantic meaning.
Fig.4: ProGuard mapping output
In the case above where ProGuard has been executed, if the apk is de-compiled, any references to the variable m_serverIpObfuscated will be displayed as f1878a.
This helps to obscure the meaning behind the code without having to worry about naming conventions disclosing their functionality.
Fig.5: Decompiled sources with obfuscated code classes/methods/fields and encrypted strings
Validate the apk
Once someone has reverse-engineered the application code, they can make malicious changes, re-compile and upload it to package repositories.
To protect against this, it is advised to self-validate at start-up and before performing any sensitive tasks. In the case self-validation is a collection of checks to ensure the configuration of the apk is as expected.
Ensure debugging flags are off. In a release application, if debugging flags are enabled, then it is possible to debug through an IDE, stepping through code and identifying variable values.
Fig.6: Example code for checking debug flags of an apk
Verify the package signature. Every android application has to be signed with a key; this should only be known by the developer or company. As a result, we can compare the key we have for our keystore to the key that was used to sign the application.
To achieve this, store an encrypted version of the key (SHA -256 hash/digest of the certificate) within the application. When this is to be checked, retrieve the current package signature SHA-256, hash it, encrypt and compare to the stored value.
Always use HTTPS and SSLSocket
Where supported by a server, HTTPS should always be used rather than HTTP, especially when handling sensitive data, such as personal user data or external IOT device commands.
Mobiles users connect to many different public Wi-Fi hotspots which might contain rogue individuals with IP packet sniffers such as Wireshark. Data downloaded through HTTP connections should be approached cautiously as it could have been tampered with.
For these reasons, SSLSockets are better than standard sockets. They provide authentication with the end point, as well as encryption of data using the transport protocol. Developers can check and verify the certificate of the server they are trying to connect to against a hash of what it should be; this prevents DNS alteration attacks, where someone routes traffic to a dummy site on a public access point.
Encrypt save data
Most of the data stored by an application is within the applications sandbox and is safe from other apps, including Android’s Shared Preferences. However, this is not true for rooted devices which can access restricted application space. This – and unsecured, re-compiled applications – is why data should never be stored as plain text.
If sensitive data does need to be stored, then it should be sufficiently encrypted using AES. If passwords are required locally as part of the application, it is better to store a hash and compare the resulting value against the result of hashing the new password.
For server communications that require password authentication, the first transaction should be to use a secure hashing function on the password (SHA-256) with the result to act as the password. This is then sent to and stored on the server and any further authentication attempts should compare the two hashes.
This approach is focused on protecting users’ data. If there is a compromise on the server, the passwords are only stored as a hash and, without the shared secret, they cannot match an input to an output.
Author profile:
Graeme Wintle is director of ByteSnap Design.