Writing a Secure Android App

by

When it comes to developing an Android app that deals with sensitive information, you must take extra precautions to make sure that the information doesn’t fall into the wrong hands. Although Android devices come with state-of-the-art security features, app developers often don’t leverage them. Even if they do, they may not necessarily be using them properly. One simple security hole is all it takes to compromise your app’s security as well as the security of the servers/systems it’s connecting to.

If you have a security concern requiring encryption, start by looking into leveraging standard security algorithms that have been well-tested and widely used. For instance, if your app needs to securely communicate with a server, use HTTPS (javax.net.ssl) instead of introducing your own protocol. If it needs to encrypt individual pieces of data, there are several strategies to consider.

Now that you’ve selected an algorithm, you must use it properly. Let’s say your app needs to encrypt data and you’ve picked a symmetric encryption AES. To encrypt, AES requires a secret key and the plain-text message to be encrypted. The encrypted message can then be decrypted only by using the same secret key. The most common mistake developers make is in how the secret key is generated and used. Consider the following code:

[code lang=”java”]<br /><br />
final int AES_KEY_LENGTH = 256;<br /><br />
char [] password = …; // obtain password from user input;<br /><br />
byte [] salt = {…};</p><br />
<p>// in this example, we’re using password-based encryption (PBE)<br /><br />
// 10000 is the number of hash iteration. Lower number for faster, less secure encryption<br /><br />
PBEKeySpec keySpec = new PBEKeySpec(password, salt, 10000, AES_KEY_LENGTH);</p><br />
<p>SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBEWITHSHAANDTWOFISH-CBC");<br /><br />
SecretKey secretKey = keyFactory.generateSecret(keyspec);<br /><br />
secretKey = new SecretKeySpec(secretKey.getEncoded, "AES");</p><br />
<p>// get initialization vector<br /><br />
byte [] ivBytes = getInitializationVector();<br /><br />
IvParameterSpec iv = new IvParameterSpec(ivBytes);<br /><br />
Cipher c = Cipher.getInstance(CIPHERMODEPADDING);<br /><br />
c.init(Cipher.ENCRYPT_MODE, secretKey, iv);</p><br />
<p>// encrypt…<br /><br />
byte [] encrypted = c.doFinal(thePlainMessage);<br /><br />
[/code]

In this example, the secret key is NOT defined as a constant nor is it read from device storage. Instead, it’s derived from a password entered by the user. This is the correct practice. Remember, you’ll need to always assume that anything stored on a device can be read one way or another by anyone who has possession of the device. So, storing a secret key in the device is not a good idea. One more thing that I’d like to point out from the above example is the initialization vector. For AES, IV plays an important role in the encryption and decryption process. An encrypted message can’t be decrypted if the IV is different. There are many ways to create the IV, but it should be as unpredictable as possible. If you prefer to use random bytes, use java.util.SecureRandom which is “cryptographically secure”.

And if you do use SecureRandom, you’ll need to be very sure that you are using it properly. Otherwise, the “random” numbers it generates could be predictable and not that random after all. When you create an instance of SecureRandom to use, it is by default seeded with the provided secure mechanism by the secure random generator provider [e.g. SecureRandom.getInstance(“SHA1PRNG”)]. However, careless developers call setSeed with either a static arbitrary number or a timestamp. This will cause the class to return a predictable sequence of numbers.

If you pay a close attention to the above code, you’d notice this: IV, password, keys are all in an array of bytes; not in the String class. Why? This is done because Java’s String class is immutable. Per Java SDK documentation, the String class doesn’t possess any method to change or clear out its contents. Let’s take a look at this code:

[code lang=”java”]<br /><br />
String pw = getPassword();<br /><br />
// …<br /><br />
// do something with pw…<br /><br />
// …<br /><br />
pw = null;<br /><br />
[/code]

At the end of the code, even though pw is now pointing to null, the initial String object that holds the password is still in memory. It will stay there until garbage collection kicks in. And even after it’s garbage collected, the actual password string may still exist in memory. As you may recall, garbage collection simply marks an address in memory as ‘available’; it doesn’t actually zero out the content.

If your app uses an UI component such as EditText for the password, always deal with the CharSequence class when reading and manipulating a user’s entry.

One last note: Review your code carefully. Try to have other software developers with a security background review the code. When it comes to security, you can never be too careful.

1 Comment

  1. George Fan on said:

    Yeah, we all know it is not safe to store the SecretKey on an device. However, this article does not give any practical solution for where it should be put (don’t tell me to store the key in user’s hands or a server or anywhere else cause it is simply not useful!).

Leave a Reply

Your email address will not be published. Required fields are marked