Posted by Trevor Johns, Android Developer Relations team
Following our talk "Security and Privacy in Android Apps" at Google I/O last year, 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 passwords and auth tokens, on local storage.
A common (but incorrect) pattern that we've recently become aware of is to use SecureRandom as a means of generating deterministic key material, which would then be used to encrypt local credential caches. Examples are not hard to find, such as here, here, here, and elsewhere.
SecureRandom
In this pattern, rather than storing an encryption key directly as a string inside an APK, the code uses a proxy string to generate the key instead — similar to a passphrase. This essentially obfuscates the key so that it's not readily visible to attackers. However, a skilled attacker would be able to easily see around this strategy. We don't recommend it.
The fact is, Android's existing security model already 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.
MODE_PRIVATE
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 code:
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 number! System.out.println(secureRandom.nextInt());
The old Bouncy Castle-based implementation allowed overriding the internally generated, /dev/urandom based key for each SecureRandom instance. Developers which attempted to explicitly seed the random number generator 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, applications who relied on the old behavior will find that the output from SecureRandom changes randomly every time their application starts up. (This is actually a very desirable trait for a random number generator!) Attempting to obfuscate encryption keys in this manner will no longer work.
A more reasonable approach is simply to generate a truly random AES key when an application is first launched:
public static SecretKey generateKey() throws NoSuchAlgorithmException { // Generate a 256-bit key final int outputKeyLength = 256; SecureRandom secureRandom = new SecureRandom(); // Do *not* seed secureRandom! Automatically seeded from system entropy. KeyGenerator keyGenerator = KeyGenerator.getInstance("AES"); keyGenerator.init(outputKeyLength, secureRandom); SecretKey key = keyGenerator.generateKey(); return key; }
Note that the security of this approach relies on safeguarding the generated key, which is is predicated on the security of the internal storage. Leaving the target file unencrypted (but set to MODE_PRIVATE) would provide similar security.
If your app needs additional encryption, a recommended approach is to require a passphase or PIN to access your application. This passphrase could be fed into PBKDF2 to generate the encryption key. (PBKDF2 is a commonly used algorithm for deriving key material from a passphrase, using a technique known as "key stretching".) Android provides an implementation of this algorithm inside SecretKeyFactory as PBKDF2WithHmacSHA1:
SecretKeyFactory
PBKDF2WithHmacSHA1
public static SecretKey generateKey(char[] passphraseOrPin, byte[] salt) throws NoSuchAlgorithmException, InvalidKeySpecException { // Number of PBKDF2 hardening rounds to use. Larger values increase // computation time. You should select a value that causes computation // to take >100ms. final int iterations = 1000; // Generate a 256-bit key final int outputKeyLength = 256; SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1"); KeySpec keySpec = new PBEKeySpec(passphraseOrPin, salt, iterations, outputKeyLength); SecretKey secretKey = secretKeyFactory.generateSecret(keySpec); return secretKey; }
The salt should be a random string, again generated 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 password hashes.
As mentioned above and in the New Security Features in Jelly Bean, the default implementation of SecureRandom is changed in Android 4.2. Using it to deterministically generate keys is no longer possible.
If you're one of the developers who's been generating keys 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.
Posted by Fred Chung, Android Developer Relations team
Android 4.2, Jelly Bean, introduced quite a few new features, and under the covers it also added a number of security enhancements to ensure a more secure environment for users and developers.
This post highlights a few of the security enhancements in Android 4.2 that are especially important for developers to be aware of and understand. Regardless whether you are targeting your app to devices running Jelly Bean or to earlier versions of Android, it's a good idea to validate these areas in order to make your app more secure and robust.
Content providers are a facility to enable data sharing amongst app and system components. Access to content providers should always be based on the principle of least privilege — that is, only grant the minimal possible access for another component to carry out the necessary tasks. You can control access to your content providers through a combination of the exported attribute in the provider declaration and app-specific permissions for reading/writing data in the provider.
exported
In the example below, the provider ReadOnlyDataContentProvider sets the exported attribute to "true", explicitly declaring that it is readable by any external app that has acquired the READ_DATA permission, and that no other components can write to it.
ReadOnlyDataContentProvider
READ_DATA
<provider android:name=”com.example.ReadOnlyDataContentProvider” android:authorities=”com.example” android:exported=”true” android:readPermission=”com.example.permission.READ_DATA” />
Since the exported attribute is an optional field, potential ambiguity arises when the field is not explicitly declared in the manifest, and that is where the behavior has changed in Android 4.2.
Prior to Jelly Bean, the default behavior of the exported field was that, if omitted, the content provider was assumed to be "exported" and accessible from other apps (subject to permissions). For example, the content provider below would be readable and writable by other apps (subject to permissions) when running on Android 4.1 or earlier. This default behavior is undesirable for sensitive data sources.
<provider android:name=”com.example.ReadOnlyDataContentProvider” android:authorities=”com.example” />
Starting in Android 4.2, the default behavior for the same provider is now “not exported”, which prevents the possibility of inadvertent data sharing when the attribute is not declared. If either the minSdkVersion or targetSdkVersion of your app is set to 17 or higher, the content provider will no longer be accessible by other apps by default.
minSdkVersion
targetSdkVersion
While this change helps to avoid inadvertent data sharing, it remains the best practice to always explicitly declare the exported attribute, as well as declaring proper permissions, to avoid confusion. In addition, we strongly encourage you to make use of Android Lint, which among other things will flag any exported content providers (implicit or explicit) that aren't protected by any permissions.
Android 4.2 includes a new default implementation of SecureRandom based on OpenSSL. In the older Bouncy Castle-based implementation, given a known seed, SecureRandom could technically (albeit incorrectly) be treated as a source of deterministic data. With the new OpenSSL-based implementation, this is no longer possible.
In general, the switch to the new SecureRandom implementation should be transparent to apps. However, if your app is relying on SecureRandom to generate deterministic data, such as keys for encrypting data, you may need to modify this area of your app. For example, if you have been using SecureRandom to retrieve keys for encrypting/decrypting content, you will need to find another means of doing that.
A recommended approach is to generate a truly random AES key upon first launch and store that key in internal storage. For more information, see the post "Using Cryptography to Store Credentials Safely".
Javascript hosted in a WebView can directly invoke methods in an app through a JavaScript interface. In Android 4.1 and earlier, you could enable this by passing an object to the addJavascriptInterface() method and ensuring that the object methods intended to be accessible from JavaScript were public.
WebView
addJavascriptInterface()
On the one hand, this was a flexible mechanism; on the other hand, any untrusted content hosted in a WebView could potentially use reflection to figure out the public methods within the JavascriptInterface object and could then make use of them.
JavascriptInterface
Beginning in Android 4.2, you will now have to explicitly annotate public methods with @JavascriptInterface in order to make them accessible from hosted JavaScript. Note that this also only takes effect only if you have set your app's minSdkVersion or targetSdkVersion to 17 or higher.
@JavascriptInterface
// Annotation is needed for SDK version 17 or above. @JavascriptInterface public void doSomething(String input) { . . . }
Android 4.2.2 introduces a new way of protecting your apps and data on compatible devices — secure USB debugging. When enabled on a device, secure debugging ensures that only host computers authorized by the user can access the internals of a USB-connected device using the ADB tool included in the Android SDK.
Secure debugging is an extension of the ADB protocol that requires hosts to authenticate before accessing any ADB services or commands. At first launch, ADB generates an RSA key pair to uniquely identifies the host. Then, when you connect a device that requires secure debugging, the system displays an authorization dialog such as the one shown below.
The user can allow USB debugging for the host for a single session or can give automatic access for all future sessions. Once a host is authorized, you can execute ADB commands for the device in the normal way. Until the device is authorized, it remains in "offline" state, as listed in the adb devices command.
adb devices
For developers, the change to USB debugging should be largely transparent. If you've updated your SDK environment to include ADB version 1.0.31 (available with SDK Platform-tools r16.0.1 and higher), all you need to do is connect and authorize your device(s). If your development device appears in "offline" state, you may need to update ADB. To so so, download the latest Platform Tools release through the SDK Manager.
Secure USB debugging is enabled in the Android 4.2.2 update that is now rolling out to Nexus devices across the world. We expect many more devices to enable secure debugging in the months ahead.
For a full list of security best practices for Android apps, make sure to take a look at the Security Tips document.