Typically Android security issues fall into a couple of major categories. Firstly, personal information stored insecurely on a phone and secondly, insecure communication to any back end database or web server. And while there are lots of other things that can go wrong, the majority of security problems fall into these two areas. In this article we will look at the various options available to secure personal information in an app, and in the next article we’ll look at network communications.
The best option is to never store a user’s personal information, such as passwords or credit card numbers, on the phone. If getting the user to enter a password each time is not an option then you’re going to have to store the username and password somewhere on the device. There really aren’t a lot of places you can store information on an Android device. The possibilities are to store the information in shared preferences, or in a sqlite database, or in the device’s keystore.
Over the past few years, I have been part of a process that manually audited a couple hundred Android apps. During that time I have seen the same security problems, repeated again and again. And although we do our best to let the developers know about the security issues with their apps, we’ve been a lot more successful at hacking the apps than at getting anyone to fix them. So in an effort to spread the knowledge, let’s look at some real world authentication patterns we’ve seen as developers try to hide password information.
These are ranked in order of difficulty to break.
- Store in cleartext
- Store encrypted using a symmetric key
- Using the Android Keystore
- Store encrypted using asymmetric keys
Using cleartext means there is no protection to a user’s runtime data – assuming the hacker has physical access to the phone, which is a big assumption. But as there are so many phones for sale on eBay and Craigslist you have to assume that your app is going to end up sooner or later on a secondhand device. You can access all the information in an app’s data folders use the
adb backup command and then convert it into a tar format using the Android Backup Extractor or abe.jar. For example:
adb backup com.packagename.android
java -jar abe.jar unpack backup.ab
tar -xvf backup.tar
Using cleartext means there is no protection to a user's runtime data - assuming the hacker has physical access to the phone.
A slightly better option used by a significant number of apps is to set the android:allowBackup flag to false in AndroidManifest.xml and then put whatever you want in the shared preferences or in into an sqlite database. The idea being that if nobody can back it up then nobody should be able to access the passwords. Unfortunately there’s a big flaw in this argument. Android is a Linux based system so root will have access to any files on the phone. If the phone isn’t properly wiped when it’s resold the new owner is going to have all the time in the world to root the phone and recover whatever dynamic user information is stored in the data folders by changing the permissions on the file and then doing an adb pull, instead of the adb backup command. Here is an example shared preferences file with an exposed password:
<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<boolean name="remember" value="true" />
A much better idea is to encrypt the password before you store it. If you are going to take this approach then don’t store the key in the APK code or anywhere else on the phone. It’s never a good idea to use AES, DES or any other symmetric encryption algorithm if you store the key where it can be easily found. It is a relatively simple process to find the APK and then get a copy off the phone. The command
adb shell pm list packages will get a list of the APK’s on your phone. To find where the APK lives on the phone use the command
adb shell pm path com.packagename.android using your APK name. Then use the following command to get a copy of the APK,
adb pull /data/app/com.packagename.android-1/base.apk making the appropriate changes for the package name and path or apk name where appropriate.
jadx base.apk to decompile the code back into java source code to see if you can find the encryption key in the code. If you are familiar with dex2jar then I suggest switching to jadx. It is an order of magnitude better at decompilation than dex2jar. Lots of apps I audited in the past with dex2jar have only given up their secrets when we starting using jadx.
Developers are a helpful bunch and they often put the encryption keys in easy to find places like com.packagename.android.util.security.
Developers are a helpful bunch and they often put the encryption keys in easy to find places like com.packagename.android.util.security. If that doesn’t work more often than not the code isn’t obfuscated and you can try searching for the class name or phrases like ‘encrypt’ or ‘decrypt’. To decrypt the password cut and paste the decryption code into a java file and give it the password as an argument. Some developers make the encryption key device specific by including device information such as the AndroidID as well as make and model information, but it’s mostly worthless if you can already see how the key is put together in the code. If you are going to use some sort of a recipe to generate the encryption key then obfuscate the code properly so that the ingredients are not easy to find. If possible store some piece of information (or even the entire key) remotely on a server, so not all the information can be found on the phone.
And as your app grows and you add more developers, make sure everyone knows how to encrypt and decrypt login information. In one of the dating apps I audited, I found the password encrypted in the shared preferences and another copy of the password was also stored in cleartext in the app’s sqlite database. Someone either didn’t know the rules or simply forgot to remove old code that stored the password in the database.
If you’re writing a healthcare app and you want to see if it’s HIPAA secure then put your device into airplane mode and if you can still log into the app then it’s probably not compliant with the HIPAA regulations.
The safest option for encrypting passwords is to use asymmetric encryption algorithms such as RSA. Asymmetric means that the key is split into public and private keys where only the private key can decrypt the information. We’re seeing a lot more developers using the Android Keystore to store public and private asymmetric key information.
In the Android Keystore this public – private key exchange takes place on the device and would seem to be HIPAA compliant. We say ‘seem to be’ as once again if you can root the phone you can gain access to the private keys. Nothing is 100% secure and sooner or later someone will find a way to get at the keys, especially if you put everything in the same place. I’ve always had a problem with mechanisms like the Android Keystore because app developer’s are relying on the skills of another developer for security, and there is no physical impediment to get at the keys.
Android Keystore public and private keys are stored in the
/data/misc/keystore/user_0 directory. The private key is stored in a file that has <app_id>_USRCERT_<key_alias>. On a rooted phone you can copy the file to another <app_id_malicious>_USRCERT_<key_alias> and then import it from your malicious app, allowing you to recover the password.
A safer asymmetric encryption option is to store the private key remotely. When the password is first entered, it is sent to the server for storage. It’s also encrypted with the public key and stored in the shared preferences. Every time the password needs to be checked then the public key encrypted password gets sent to the backend server and decrypted by the private key. It is then checked against the password information in the server’s database. A token is then passed to the Android client to allow access to the app. At no time is the password visible on the phone.
In Android this usually means using the Spongy Castle libraries or other alternative such as Google’s Keyczar. Sure there are other security issues that you have to think about like how to ensure that someone isn’t just sending you a publically encrypted key from a different device. But it is a lot easier to add extra ingredients to your asymmetric key recipe to foil these type of attacks when the code is on the server. We’ll return to this in the next article.
In the future Lollipop device encryption may put an end to many of these types of attack, but until Lollipop gains critical mass then options 1, 2 and 3 are not secure approaches. Our original recommendation is to ask the user to enter their password every time they sign in. If that’s not possible then never store the password in cleartext or leave the key in the code or on the device for someone to find. Asymmetric encryption keys using Spongy Castle or Google Keyczar are much better alternatives to consider. In the next article we’ll look at similar options for safeguarding your API keys.
About the author