Tuesday, May 24, 2016

Using Cryptography to Store Credentials Safely


Posted by Trevor Johns, Android Developer Relations tm


Following our talk "Security and Privacy in Android Apps" at Google I/O last yr, many people had specific questions about how to use cryptography in Android. Many of those revolved around which APIs to use for a specific purpose. Let's look at how to use cryptography to safely store user credentials, such as s and auth tokens, on local storage.

An anti-pattern

A common (but incorrect) pattern that we've recently become aware of is to use SecureRandom as a mns of erating deterministic material, which would then be used to encrypt local credential caches. Examples are not hard to find, such as here, here, here, and elsewhere.

In this pattern, rather than storing an encryption directly as a string inside an APK, the uses a proxy string to erate the instd — similar to a passphrase. This essentially obfuses the so that it's not rdily visible to attackers. However, a skilled attacker would be able to sily see around this strategy. We don't recommend it.

The fact is, Android's existing security model alrdy provides plenty of protection for this kind of data. User credentials should be stored with the MODE_PRIVATE flag set and stored in internal storage, rather than on an SD card, since permissions aren't enforced on external storage. Combined with device encryption, this provides protection from most types of attacks targeting credentials.

However, there's another problem with using SecureRandom in the way described above. Starting with Android 4.2, the default
SecureRandom provider is OpenSSL, and a developer can no longer override SecureRandom’s internal state. Consider the following :


SecureRandom secureRandom = new SecureRandom();
byte[] b = new byte[] { (byte) 1 };
secureRandom.setSeed(b);
// Prior to Android 4.2, the next line would always return the same !
System.out.println(secureRandom.nextInt());


The old Bouncy Castle-based implementation allowed overriding the internally erated, /dev/urandom based for ch SecureRandom instance. Developers which attempted to explicitly seed the random would find that their seed replaces, not supplements, the existing seed (contrary to the reference implementation’s documentation). Under OpenSSL, this error-prone behavior is no longer possible.

Unfortunately, appliions who relied on the old behavior will find that the output from SecureRandom changes randomly every time their appliion starts up. (This is actually a very desirable trait for a random !) Attempting to obfuse encryption in this manner will no longer work.

The right way

A more rsonable approach is simply to erate a truly random AES when an appliion is first launched:

public static Secret erate() throws NoSuchAorithmException {
// erate a 256-bit
final int outputLength = 256;

SecureRandom secureRandom = new SecureRandom();
// Do *not* seed secureRandom! Automatically seeded from system entropy.
= .getInstance("AES");
.init(outputLength, secureRandom);
Secret = .erate();
return ;
}

Note that the security of this approach relies on safeguarding the erated , which is is predied on the security of the internal storage. Lving the target file unencrypted (but set to MODE_PRIVATE) would provide similar security.

Even more security

If your app needs additional encryption, a recommended approach is to require a passphase or PIN to access your appliion. This passphrase could be fed into PBKDF2 to erate the encryption . (PBKDF2 is a commonly used aorithm for deriving material from a passphrase, using a technique known as " stretching".) Android provides an implementation of this aorithm inside SecretFactory as PBKDF2WithHmacSHA1:

public static Secret erate(char[] passphraseOrPin, byte[] salt) throws NoSuchAorithmException, InvalidSpecException {
// of PBKDF2 hardening rounds to use. Larger values incrse
// computation time. You should select a value that causes computation
// to take >100ms.
final int iterations = 1000;

// erate a 256-bit
final int outputLength = 256;

SecretFactory secretFactory = SecretFactory.getInstance("PBKDF2WithHmacSHA1");
Spec Spec = new PBESpec(passphraseOrPin, salt, iterations, outputLength);
Secret secret = secretFactory.erateSecret(Spec);
return secret;
}

The salt should be a random string, again erated using SecureRandom and persisted on internal storage alongside any encrypted data. This is important to mitigate the risk of attackers using a rainbow table to precompute hashes.

Check your apps for proper use of SecureRandom

As mentioned above and in the New Security Ftures in Jelly Bn, the default implementation of SecureRandom is changed in Android 4.2. Using it to deterministically erate is no longer possible.

If you're one of the developers who's been erating the wrong way, we recommend upgrading your app today to prevent subtle problems as more users upgrade to devices running Android 4.2 or later.
Join the discussion on



+Android Developers

No comments:

Post a Comment