WHAT’S IN THIS CHAPTER?
WROX.COM CODE DOWNLOADS FOR THIS CHAPTER
Please note that all the code examples in this chapter are available at https://github.com/wileyenterpriseandroid/Examples.git and as a part of the book’s code download at www.wrox.com on the Download Code tab.
Mobile devices present one of the most profitable hacking targets in the average person’s digital world. They contain copious amounts of personal information, often include access to bank and retirement accounts, contain all your contacts, and can access your e-mail. They can also fall, without a sound, out of your pocket. They know where you are, can see your face, and can eavesdrop on your conversations while they sit in your backpack. They might also randomly load malicious code from the phones of other people as they walk nearby in the street. Clearly, the stakes in securing mobile applications are high.
Hacking for profit or espionage has become big business worldwide. Keeping user data safe and developer assets secure requires vigilance on the part of mobile developers and handset owners. This chapter covers Android techniques for enhancing security for both handset users and application developers regarding the following topics:
The previous paragraphs painted a potentially bleak picture of mobile security, but of course those scenarios depend on circumventing Android platform security controls. Mobile device features are generally safe as long as malware does not subvert formidable operating system and application protections.
When confronting long-standing security problems, recent mobile operating systems employ defenses that are leaps and bounds better than security techniques used in traditional desktop platforms like Windows and Mac OS. One of the most common ways to get a desktop virus is to unwittingly open a malicious e-mail attachment. Another pitfall is to allow insecure plugins to run in your web browser — desktop Java Technology from Oracle has recently encountered a string of serious and high-profile security flaws related to Java running in web pages. For years, Windows would run all programs downloaded from the Internet or even from floppy disks with administrative privileges. Once activated a virus could access sensitive system resources at will. Windows XP corrected this problem with user and file system permissions, but many users still ran all programs as an administrative user, thus nullifying the security enhancements. Linux and Mac OSX have always run user-level processes with a vastly reduced level of access — without admin access.
Android takes OS-level protection a step further, by extending the security model already found on Linux and Mac OS to drastically increase the level of isolation between individual applications. Each application runs as an entirely different UNIX user. This simple but effective step uses the base operating system to prevent applications from tampering with each other’s data in the file system, from accessing shared pipes, and a range of Linux resources.
Android also supports application permissions that provide another level of security not found on a platform level in traditional operating systems. Users don’t need to fully trust application code; instead, they can rely on OS-imposed limitations to prevent apps from performing sensitive operations, such as covertly slurping a user’s contacts list out of a device.
In spite of these improvements, modern mobile attacks take many forms. For example, there was a recent spate of hijacked, Trojan applications posing as popular Android applications, such as “Angry Birds”, which caused phones to participate in a spam botnet. The hijacked phones sent out prolific messages at the expense of the end user. Despite enhanced platform security, downloading malicious code onto a device is still a primary concern.
Before you begin writing secure code for Android applications, it’s important to be aware of the tools that end users can use to keep their handsets safe and to know which of these tools help developers write secure applications. Before walking through a set of Android security examples, the chapter covers techniques for safe usage of Android by end users. This involves a discussion of several security tools from Google and other vendors, all of which impact the security of the Android ecosystem.
For several years, the iPhone was king of the mobile application space. As of the time of writing of this book, Android had caught up significantly and was closing in on 800,000 applications listed in the Google Play store. With that many applications, simple probability gives hackers many chances to sneak code onto user’s phones.
Attacks on Android systems have several common sources:
Secure usage is currently the first defense against these types of attacks. We want to take a look at platform and ecosystem tools that can help handset users keep their phones safe.
If your Android device does not have the Chrome browser pre-installed, you should install it. It’s available in the Google Play store. Many attacks against applications involve cooperation on the part of the browser — like an app that directs the browser to malicious websites. Chrome helps to close holes of this nature with built-in malware and phishing protection and — of critical importance — process-level sandboxing support. Chrome is widely regarded to be one of the most, if not the most, secure browser.
To protect users from application threats, Google created the Bouncer program, which is an application that emulates applications to look for malware patterns. The program prevents malicious applicants from gaining entrance to the Google Play application store. Users and developers don’t need to do anything specific to take advantage of Bouncer’s protection — apps are rejected on store submission if they don’t pass security tests when running on the Bouncer emulator.
Although the Google Play store and the Amazon app store provide the “front door” for application installation on Android devices, Android also supports application installation through SD card side loading, as well as the ability to install applications from unknown locations. As many Android developers know, you can get to this location using the following:
Settings -> Applications -> Check Allow "Unknown Sources"
On Android 4.0 or greater use:
Settings -> Security -> Check "Unknown Sources"
Of course, users should keep in mind that although there may be good reasons to download Android applications from unknown sources, careless downloading of applications not fully known to the user is a great way to compromise phone security. For example, one spam botnet attack, that Lookout Security gave the moniker SpamSoldier, spread itself by sending SMS messages with a link for unsuspecting users to download — not particularly sophisticated, but successful against teenagers and other newbies using phones.
Although you might think you know an application from an “unknown source” well enough to install it outside of the store, it’s smart to check it for viruses just in case. As of Android 4.1/4.2, Jellybean, on Google Nexus, Google has provided users with an additional security check that blocks malicious apps from installation, called the application verifier service. The feature is active by default, but users can turn it off, at their own risk, with:
Settings -> Security -> Verify Apps
When you install a new app — from any app store, or “unknown sources”, not just Google Play — the verifier service will collect information about the application. If the Google cloud has information that the application is malicious, it will take one of two actions. If the app is dangerous, Android will not install it. If the app is potentially dangerous, it will warn the user and provide the option of not installing it. You can find instructions for turning on the verifier service for the Google Nexus below:
https://support.google.com/nexus/galaxy/answer/2812853?hl=en-CA
Now that you have some awareness of major security measures that Google has put in place for Android, it’s time to take a look at publicly available security reviews of them:
Security researchers initially investigated Google Bouncer and found that it was possible to “fingerprint” the service, which meant that it was possible, at one point in time, for applications under review to investigate the Bouncer environment and report back this useful information about it to potentially malicious authors. You can find more information about the review here:
http://blog.trendmicro.com/trendlabs-security-intelligence/a-look-at-google-bouncer/
Such transparency might open the door to security attacks.
As of November 2012, another security researcher investigated the Nexus Application Verifier Service and presented his results online, along with a description of the App Verifier tool:
http://www.cs.ncsu.edu/faculty/jiang/appverify/
This researcher, a founder of the Android Malware Genome project, attempted to install a large number of known malicious Android applications. Alarmingly, the researcher found a low detection rate of < 20 percent. The results of this research indicate that even with Google’s security measures, users should approach the installation of Android applications with a strong sense of caution. Still, keep in mind that this work was very early in the life of the verifier service, and Google is likely to improve it.
Although Google and other vendors have made considerable progress in protecting user security, these results indicate that device users should know enough about the applications and their corresponding developers to self-curate the apps that they install on their phones. According to sources online, detection rates for malicious apps are well below 100 percent — users should not operate under the assumption that all applications in Android app stores are safe.
Users who have Google Apps for Business accounts can take advantage of additional Android Security features, such as requiring a screen lock and pin or password for users, and remote wipe for lost or stolen phones. Users also get the ability to remotely:
After you have a Google Apps for Business account, you also need to download the Device Policy application. You can get this application, like any other, from the Google Play store. Once you have installed it, if you are not signed into your Google Apps account, you will need to add the account to the Device Policy application. Google has provided instructions for installing the Device Policy app:
http://support.google.com/a/users/bin/answer.py?hl=en&answer=2364439
Once the Device Policy is installed, you can set up the device management features:
http://support.google.com/a/users/bin/answer.py?hl=en&answer=1235372&topic=2365092&ctx=topic
Beyond tools from Google, the rising need for Android security has bred an industry in advanced mobile application security protection. Some of the more successful companies in this space include Lookout, Appthority, and Bit9. The main features of many of these applications include:
According to Lookout Security, the most prevalent form of Android malware takes the form of toll fraud, which is a covert and particularly common way of sneaking charges into a subscriber’s cell phone bill — 72 percent of Lookout-detected malware involved toll fraud. Typically, toll fraud involves billable SMS messages, which is a common way of paying for mobile transactions. Cell phones have supported text-based billing for many years to enable users to buy ring tones, images, and so on.
An example of toll fraud involves the installation of a malicious app that can hide and respond to billable SMS messages. Such an application will work with a cooperating backend that can receive malicious SMS messages sent from the app. The carrier ends up allowing a charge from the malicious service, since the malicious app is able to receive SMS confirmation messages. The carrier interprets access to a device as confirmation of a user’s identity. With an insidious twist, the toll fraud application will hide the confirmation SMS message so that the handset user never even sees it. The only way that users will ever know that theft has occurred is by paying careful attention to their cell phone bills. For more information, check out the following:
http://bits.blogs.nytimes.com/2012/12/13/lookout-toll-fraud/
Another promising Android security investigation called the “malware genome” analyzed known Android exploits to discover their modes of operation and the security flaws in Android and applications that they exploit. A brief summary of the findings concerning malware includes:
To give you an idea of what’s involved in detecting malicious apps, here’s a list of the prominent types of attacks:
Compared to traditional desktop systems, Android certainly has improved OS-level protection, but mobile software supports complex and detailed data about its users and faces increasingly sophisticated attacks trying to access that lucrative information. Developers writing applications for Android need to consider security upfront and be sure that they write secure code from the first deployment of their application. It’s time to shift focus from keeping malicious code off Android to defending your own application in an environment where malicious code might be present.
When it makes its way onto a device, malware seeks access to the system as a whole or to individually installed applications, including the ones you develop. To defend your application, consider these high-value targets that hackers are likely to attack:
Before getting too paranoid about the number of possible security holes, keep in mind that most Android settings — including file permissions — have secure default values. Android also protects applications using the Linux file system, process model, and a strong security sandbox. Android employs the following technologies to avoid traditional penetrations due to memory: SLR, NX, ProPolice, safe_iop, OpenBSD dlmalloc, OpenBSD calloc, and Linux mmap_min_addr. Android also has interesting security tricks up its sleeve, like the ability to encrypt an entire file system volume and more described in the following location:
http://developer.android.com/training/articles/security-tips.html
The first step in designing any secure application is to consider the principle of least privilege, which means that applications request only the minimum access to sensitive resources and privileges required for an app to perform its functions. For example, if you’ve written a content provider that needs to be used only in its declaring apk, don’t export the provider to other processes. Instead use android:exported="false". If external applications do need to read from the content provider, allow only read access, instead of requiring read/write access by default. If you are writing a location-aware application that does not need incredibly precise location information, request ACCESS_COARSE_LOCATION instead of ACCESS_FINE_LOCATION. And so on...
Android app developers have important reasons for writing applications that request only the permissions they need:
Keep the principle of least privilege in mind, as you read the rest of the chapter, which covers the “ingredients” of a secure application that successfully defend against security attacks. These ingredients include:
As you discovered when writing your first Android application, you must declare permissions for features, such as Internet access, used in a given application. Applications use these declarations to request the use of permissions from the user installing an application. Explicitly asking end users to grant all permissions used in an app has some associated controversy. On one hand, a careful user has the opportunity to pick up on strange access requests (such as a to-do list application that asks permission to send SMS messages and to manage Android accounts). On the other hand, perhaps the majority of handset users will not have the technical savvy to know why some permissions might be sensitive, and might get tired of trying to review information they do not understand. On the whole, requiring apps to be open with the capabilities they require will almost certainly lead to informed scrutiny from some portion of each application’s user base.
When writing Android applications that handle sensitive data, it’s important to understand the power of the Android permission system: It’s a fine-grained and powerful capability-granting framework. The Android permission system provides another strong example of how the Android platform has improved security over traditional operating systems — applications for the same user don’t automatically get access to each other’s resources or capabilities. Android enforces strong separation between all running processes.
Permissions are likely the most prominent of Android security features that app developers need to consider when developing applications. Developers should keep a vigilant eye on which privileges they request, and how they make them available to other applications. This section provides an overview of how permissions work and includes interesting details of important permissions, including a sense of their comparative danger. This section also shows you when and how to create your own custom permissions.
To review, a permission on Android protects calls that can perform security sensitive operations like sending an SMS message. Permissions can also protect calls that application developers deem sensitive. If a caller invokes a method but does not have the requisite permission, an instance of java.lang.SecurityException will be thrown, preventing the call from taking place. Applications request use of permissions in their AndroidManifest, as follows:
<uses-permission android:name="android.permission.INTERNET" />
Upon installation, end users review the list of permissions an app has requested and can reject the app if they deem the list suspicious.
The base Android operating system provides a wide array of permissions that protect the functions available on Android devices. You can find them all listed in the class android.Manifest.permission, as follows:
http://developer.android.com/reference/android/Manifest.permission.html
Every Android permission, including custom application permissions, has an associated protection level that gives users and developers a sense of how concerned they should be about granting and asking to use the permission. For example, the permission android.permission.BRICK clearly poses a lot of danger to a device, and is considered a “Dangerous” permission. A declaration of a permission in an AndroidManifest file includes specification of a protection level, as follows:
android:protectionLevel =
"normal" | "dangerous" | "signature" | "signatureOrSystem"
The following table explains protection levels in detail:
PROTECTION LEVEL | DESCRIPTION |
Normal | Minimal level threat that does not pose significant danger to the user or the system. Automatically granted on app installation. |
Dangerous | Poses significant threat to the system and to user data. Dangerous permissions require user approval and are generally subject to a large community of users who will spot suspicious permission requests. |
Signature | A permission that Android grants only to applications that have the same signature as the app that defined the permission. Automatically granted on signature match. |
SignatureOrSystem | Reserved for system use; your application should not use this level. |
But what practical knowledge do these levels provide? As you can see, Android end users must manually approve all dangerous permissions in your applications, and you should be aware of the ones that will raise eyebrows so you can work to avoid them. Of critical importance are the permissions users don’t want to see when they install your application; you need to convince users that it is okay to download your application.
Many of the permissions that should set off red flags for savvy Android users relate to the hacking targets that we have outlined in this chapter. Access to billable events, private data, sensitive user information, and security keys all have significant potential for exploitation. Here are some comments from the malware genome to give you a sense of the priorities of malware applications:
Users should install only well known apps, like Google Voice, from trusted vendors like Google that seem to have a very good reason for accessing particularly lucrative target permissions like CALL_PHONE and CALL_PRIVILEGED or the ability access text messages. If a puzzle game requires access to send text messages, it may not be a good idea to download and install it.
The following table lists the top Android permissions requested in malware applications (as provided by the Malware Genome Project) and notes vulnerabilities of each of them.
PERMISSION NAME | ACCESS NOTES |
INTERNET | INTERNET permission is useful to attackers in myriad different ways; here are a few popular ones:
1. Allows an application to open Internet sockets to any outgoing host; contrast to web-based JavaScript which can read responses from connections to its host of origin, excluding HTML5 Cross-Origin Resource Sharing (CORS), which can be as permissive as Android INTERNET permission.
2. Enables a botnet to communicate with its command and control (C&C) server.
3. A common hack directs browser and apps that have INTERNET permission to load malicious website URLs — more on this in the section, “Checking the Caller’s Permissions” later in the chapter.
4. Surprisingly, Android does not require permission to access a user’s photos. Any app that has INTERNET permission can upload user photos to the site of their choice on the Internet: http://bits.blogs.nytimes.com/2012/03/01/android-photos/ |
READ_PHONE_STATE | Provides read-only access to phone state. |
ACCESS_NETWORK_STATE | Allows applications to access information about networks. |
WRITE_EXTERNAL_STORAGE | Provides the ability to write to the SD card. Example use: to hide the location of a C&C server. |
ACCESS_WIFI_STATE | Allows access to information about WiFi networks. Potentially related to root-level exploits. |
READ_SMS | Allows applications to read existing SMS messages that are for other applications; a nice way to get at text sensitive information. |
RECEIVE_BOOT_COMPLETED | Requested five times more frequently by malware than legitimate applications. |
WRITE_SMS | Allows an application to write SMS messages. |
SEND_SMS | Allows an application to send an outgoing SMS, as would have been required by SpamSoldier. Receiving an SMS is a chargeable event, which makes it an attractive target to thieves. Sending SMS messages is especially attractive given the prevalence of toll fraud schemes based on carrier premium SMS messages. |
RECEIVE_SMS | Allows applications to receive SMS messages as required for an SMS based push notification system. Receiving an SMS is a chargeable event, which makes the action attractive to thieves. |
VIBRATE | Allows an application to turn on the phone’s vibrator. |
ACCESS_COARSE_LOCATION | Provides access to the phone’s coarse or network location — the location value that does not come from the GPS radio. |
READ_CONTACTS | Provides access to a user’s contacts; clearly valuable information for a hacker. |
ACCESS_FINE_LOCATION | Provides access to fine-grained GPS information; higher resolution than coarse network location. |
WAKE_LOCK | Prevents the phone from sleeping or the screen from dimming. This plays into the strategy of malicious applications using the phone for botnets that need the ability to run in the background without the user’s knowledge. |
CALL_PHONE | Enables outbound calls potentially to premium phone services, likely outside the United States, which can be associated with significant per instance charges. |
CHANGE_WIFI_STATE | The ability to change WiFi state is related to system events that enable a malicious application to execute root-level exploits. |
WRITE_CONTACTS | The ability to add contacts is useful for malicious apps in a number of ways — for example, to write a contact that looks like someone they recognize, but perhaps representing a malicious phone number. |
WRITE_APN_SETTINGS | Allows applications to write the GSM Access Point Name (APN) settings. |
RESTART_PACKAGES | The permission is deprecated, and applications should not use it. |
Protecting sensitive resources in your own applications, specifically to protect IPC access to your Android components such as content providers or services, may require the use of application-defined permissions. The manifest declaration in Listing 12-1 creates a permission for reading contacts from the contacts’ content provider.
LISTING 12-1: Create a custom permission for accessing contacts
<permission
android:name="com.enterpriseandroid.permission.READ_CONTACTS"
android:label="Read contact information."
android:description=
"Enables reading contacts from the contacts content provider."
android:protectionLevel="dangerous"
android:permissionGroup="android.permission-group.PERSONAL_INFO"
/>
Enacting the newly defined permission requires checking the required permission when sensitive code is called, as in Listing 12-2.
LISTING 12-2: Checking an application-defined permission
PackageManager manager = getPackageManager();
int hasPermission =
manager.checkPermission(
"com.enterpriseandroid.permission.READ_CONTACTS",
"com.enterpriseandroid.androidSecurity");
if (hasPermission != PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("Permission Denied: Reading Contacts.");
}
This snippet of code resides in an application method that needs to read enterpriseandroid contacts. To be secure, the method should verify that a caller also has permission to read this sensitive information by comparing the return value of checkPermission to PERMISSION_GRANTED. If either the calling code or the method code do not have permission, then the method should fail by throwing the security exception.
As this book has discussed, typical places to store data include files in the file system (both internal flash storage and external SD card devices), SQLite databases, and content providers. Different Android components and files systems require different handling to maintain security. The next several sections cover securing Android file systems and application components.
Android extends the Linux security system and protects files using the base Linux file system. In Linux, file permissions follow a standard format based on binary settings, where individual bits represent whether a given file is readable, writeable, or executable by the file owner, the file group, or by all users. As an example, a file with permissions, (7, 5, 5) or (111, 110, 110) would be readable, writeable, and executable by the owner, and readable and executable by everyone else.
Android relies on the core Java API, specifically java.io.File, to enable programmatic control over file system permissions. The following methods on File change underlying Linux file permissions:
setReadable(boolean readable, boolean ownerOnly)
setWriteable(boolean writeable, boolean ownerOnly)
The following sequence of invocations:
File myFile = new File(...);
myFile.setReadable(true, false);
myFile.setWriteable(true, false);
results in a Linux permission setting of (110,000,000) or (600), which is a secure access setting that allows only the application to read and write a file stored in an application’s internal storage.
Android internal storage supports Linux file system permissions. Applications writing files to their data directory, located at /data/data/<manifest_package_name>, can make use of Linux file permissions as described in the previous section. Trolling for inadvertently accessible files is a popular technique for malware to gain access to sensitive data. In almost all cases, applications should create files that are readable and writeable only by the application itself. Keep the following in mind when setting file permissions:
As you learned when deploying your first Android application, all applications that deploy to the Google Play store must have a digital signature. These signatures fulfill several roles for Android applications and the Android platform. In many cases, they prevent malicious applications from pretending to be better known and legitimate applications. They also prevent masquerading applications from “upgrading” in place over the presence of existing applications that may have already written data. For example, if a banking application wrote data into files on Android internal storage, Android does not permit a malicious application claiming to be an upgrade for the legitimate application to access the files.
External storage devices on Android, such as removable SD cards, don’t support Linux file system permissions for the simple reason that the file system format of most SD cards is a legacy format initially created for DOS, called FAT, which does not support permissions on individual files. Storing sensitive information in files on external storage is usually not a good idea since they can only have world readable permissions and will persist even after the writing application is uninstalled from the device. If you do need to store information on the SD card, it’s a good idea to protect it using encryption.
Even if a file resides in internal storage with proper file system permissions, it’s still a good idea to encrypt its data. File system permissions will not protect bank accounts, passwords, and other information when thieves steal and jail-break phones. It’s fairly straightforward to encrypt data using the Android API, but it definitely helps to have basic knowledge of encryption to decide which algorithms you should use to secure data in files, and in RESTful calls (covered a bit later in this chapter).
The field of computer cryptography has a long and diverse history, going as far back as ciphers used by the Roman military and code breaking machines used during World War II. Encryption is the process of applying a mathematical algorithm to data to yield output data that is unintelligible to anyone who does not have a valid decrypting key. If you have this key, you can produce the original data by applying a decrypting algorithm to the encrypted data.
Modern encryption algorithms typically take symmetric or asymmetric forms:
You can find symmetric and asymmetric encryption applied in modern secure software. Public key cryptography has the significant advantage that two parties can securely exchange data without having to transfer the private key over the Internet. It’s possible to send encrypted data using a widely published public key, to a host that has access to the unpublished private key. Clients encrypt data using the public key, and only the receiving host that has the private key can decrypt the data. Public key cryptography in the form of Rivest, Shamir, Adleman (RSA) key exchange is the core of the more commonly recognized SSL/TLS and HTTPS protocols.
The main use of asymmetric encryption is for sending data over the Internet. The advantage of symmetric key encryption is its speed; it’s significantly faster than asymmetric encryption. If you need to encrypt data for local file system storage, you would likely use a symmetric algorithm.
Modern encryption algorithms rely on the computational difficulty of guessing unencrypted data from its encrypted counterpart. The data from computationally complex encryption algorithms become exponentially more difficult to decrypt by brute force means as the size of the encrypting key grows. Over the years, the size of keys has grown from 56 bits to the now commonplace 256 bits.
The need to determine the identity of arbitrary binary information arises for some important tasks in mobile and service development, such as verifying files are not modified during download and determining uniqueness of files in large file sets. Using a secure hashing function provides an answer for this type of problem. A secure hash, or digest, converts data into a single fixed length value, much smaller than the data itself. A good hash function makes it unlikely that digests for various files collide — have the same binary digest value.
Another and perhaps more important use for hashing is for the secure storage of passwords. Android applications often need to use passwords to access symmetrically encrypted files and to provide credentials for accessing RESTful web services. As noted, hackers often seek to acquire these passwords to gain the same access as legitimate application code. Consequently, you should not store user credentials for a RESTful service or an encrypted file on a device. Google recommends performing an initial authentication using credentials your app collects from the user, and then using a short-lived, service-specific authorization token (using OAuth or the account manager).
If an application finds it needs to roll its own password storage, it should hash passwords and store their hashed digest — the output of the hash function. When users give their passwords to an application, the application will hash them, and then compare them against already stored digests. If it finds a match, the application accepts the user’s credentials. Modern password storage approaches should include random values, called salt, along with the password and hash both together repeatedly.
A few encryption techniques stand out as particularly relevant for writing enterprise Android applications:
It’s time to get started with the first of the chapter examples. This code demonstrates the use of symmetric encryption based on the AES algorithm. You can run the AESEncryption Activity in the sample code to show a string, "This is a demo message from Java!" encrypted and then decrypted. If you need to secure data, you should encrypt data as needed and then write the encrypted text into files as appropriate for your application.
The class AESEncryptionHelper has methods to encrypt and decrypt an array of bytes, as shown in Listing 12-3:
LISTING 12-3: Symmetric encryption on Android (AES-256)
package com.enterpriseandroid.androidSecurity;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.CipherOutputStream;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
/**
* Demonstrates the use of symmetric encryption on Android (AES-256)
*/
public class AESEncryptionHelper {
private String padding =
"ISO10126Padding"; //"ISO10126Padding", "PKCS5Padding"
private byte[] iv;
private byte[] key;
private Cipher encryptCipher;
private Cipher decryptCipher;
public AESEncryptionHelper(byte[] key, byte[] iv) throws Exception {
this.key = key;
this.iv = iv;
initEncryptor();
initDecryptor();
}
private void initEncryptor() throws NoSuchAlgorithmException,
NoSuchPaddingException, InvalidKeyException,
InvalidAlgorithmParameterException
{
SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
IvParameterSpec ivSpec = new IvParameterSpec(iv);
Initialize the encryption cipher used to write encryption bytes:
encryptCipher = Cipher.getInstance("AES/CBC/" + padding);
encryptCipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec);
}
private void initDecryptor() throws Exception{
SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
IvParameterSpec ivSpec = new IvParameterSpec(iv);
Initialize the decryption cipher used to write encryption bytes:
decryptCipher = Cipher.getInstance("AES/CBC/" + padding);
decryptCipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
}
This is a generic method for encrypting an array of bytes and it works by writing all bytes to a CipherInputStream that has been configured for AES. The encrypted bytes collect into a byte array output stream, which is converted into a byte[] as a return value.
public byte[] encrypt(byte[] dataBytes) throws IOException{
ByteArrayInputStream bIn =
new ByteArrayInputStream(dataBytes);
@SuppressWarnings("resource")
CipherInputStream cIn =
new CipherInputStream(bIn, encryptCipher);
ByteArrayOutputStream bOut =
new ByteArrayOutputStream();
int ch;
while ((ch = cIn.read()) >= 0) {
bOut.write(ch);
}
return bOut.toByteArray();
}
Here is another generic method, this time for decrypting an array of bytes; it works by writing all bytes to a CipherOutputStream that has been configured for AES. As before, the encrypted bytes collect into a byte array output stream, which is converted into a byte[] as a return value.
public byte[] decrypt(byte[] dataBytes) throws IOException {
ByteArrayOutputStream bOut = new ByteArrayOutputStream();
CipherOutputStream cOut =
new CipherOutputStream(bOut, decryptCipher);
cOut.write(dataBytes);
cOut.close();
return bOut.toByteArray();
}
public static void main(String[] args) throws Exception {
The message to encrypt:
String demoMessage =
"This is a demo message from Java!";
byte[] demoMesageBytes =
demoMessage.getBytes();
//shared secret
byte[] demoKeyBytes = "abcdefghijklmnop".getBytes();
// Initialization Vector - usually contains random data along
// with a shared secret or transmitted along with a message.
// Not all the ciphers require IV - we use IV in this
// particular sample
byte[] demoIVBytes =
new byte[] {
0x00, 0x01, 0x02, 0x03,
0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0a, 0x0b,
0x0c, 0x0d, 0x0e, 0x0f};
AESEncryptionHelper aesHelper =
new AESEncryptionHelper(demoKeyBytes, demoIVBytes);
First encrypt the bytes:
byte[] encryptedMsg =
aesHelper.encrypt(demoMesageBytes);
System.out.println("Encrypted Msg: " +
new String(encryptedMsg));
Print the encrypted data:
byte[] decryptedMsg =
aesHelper.decrypt(encryptedMsg);
Print the decrypted data:
System.out.println("Decrypted Msg: " +
new String(decryptedMsg));
}
}
Now that you can encrypt and decrypt files using AES, it’s time to think about how to handle passwords.
The next code example shows how an application uses a hashed password as a key to a symmetrically encrypted data file, which was created in the previous example. The example hashes the password with salt created from a supplied username. A real-world application of this idea would store the password on a remote service to avoid the security risk of saving the password in the local file system. To decrypt a file with such a key, an application would download the remote password, rehash it, and then decrypt the relevant data. Listing 12-4 generates salt and then performs a shaHex on the password and the salt.
LISTING 12-4: Password hashing
package com.enterpriseandroid.androidSecurity;
import org.apache.commons.codec.digest.DigestUtils;
import android.util.Log;
public class PasswordHelper {
private static final String TAG = "PasswordHelper";
private String passwordHash;
public PasswordHelper(String username, String password) {
Log.i(TAG, "*****username:" + username + " password:" + password);
String salt = generateSalt(username);
Log.i(TAG, "*****salt:" + salt );
Generate the password digest:
this.passwordHash = DigestUtils.shaHex(password + salt);
Log.i(TAG, " hash:" + passwordHash);
}
Generate salt:
private String generateSalt(String s) {
StringBuffer buf = new StringBuffer();
for( int i=0; i< s.length(); i++) {
if ( i % 2 ==0 ) {
buf.append(s.charAt(i));
}
}
return buf.toString();
}
public String getPasswordHash() {
return passwordHash;
}
Perform a repeat hash as would be required when downloading a password for rehashing to unlock decrypted data:
public boolean validatePassword(String username, String password) {
Log.i(TAG, "username:" + username + " password:" + password);
String salt = generateSalt(username);
Log.i(TAG, " salt:" + salt );
Log.i(TAG, " hash:" + passwordHash);
Log.i(TAG, "validate hash:" + DigestUtils.shaHex(password + salt));
return passwordHash.equals(DigestUtils.shaHex(password + salt));
}
}
Enterprise and otherwise security conscious users can attain peace of mind by simply encrypting all user data on their devices. Android supports volume-wide data encryption, based on a required phone pin or password (screen lock is not supported). It’s simple to enable file system encryption. Make sure your phone is charged and plugged in, and then select the option with:
Settings -> Security -> Encrypt Phone
SQL injection attacks are likely the most significant risk to data in a database. This type of attack is easy for hackers to mount and has traditionally yielded significant low-hanging fruit. SQL injection applies to backend services implemented with Hibernate in SQL, but also to Android applications that use SQLite. Chapter 3 noted the peril of manually composing strings to create SQL queries. Recall that the simple use of query, ?, parameters avoids such attacks. In backend services based on Hibernate, a similar approach enables prevention of most injection attacks. Recall the discussion from earlier chapters regarding usage of Android data APIs, specifically where usage of the method:
SQLiteDatabase.query(String table,
String[] columns,
String selection,
String[] selectionArgs,
String groupBy,
String having,
String orderBy,
String limit)
should always make use of the selectionArgs parameter to replace embedded query occurrences of "?" Developers should never build queries by appending search parameters into a large string. Doing so robs the underlying database engine of the ability to determine the bounds of input parameters, which allows those parameters to include SQL designed to get the database to return results that would otherwise never be part of a response.
Android supports a rich interprocess communication model, including Remote Procedure Calls (RPC) based on AIDL, intent-based invocation and broadcasting, and a service model for running tasks in the background. The next sections cover how to build security defenses for these components.
The Android Intent system supports generic messaging for Android components. The Intent object carries with it a logical operation to perform (such as, take a picture) and arguments to use (like a URI). When you wrote your first Android application, you declared an intent filter to decide which activity would handle starting your application, as follows:
<activity android:name=".YourActivity" android:label="@string/your_app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
A declaration of an intent filter means that Android should send intents to the declaring component. This opens the door to potential attacks; malicious apps can send intents as easily as legitimate ones. The easiest, but least functional, way to shut them down is to disallow cross process intents by marking components as not exported in your application’s manifest, as follows:
<service android:exported="false"></service>
However, for components that you do export, if malicious code launches your application with attack arguments, it should not end up doing the hacker’s bidding. An intent filter is a good start to adding security. Protect your components by specifying a filter that lists the intents your component can handle, as follows:
<intent-filter>
<action android:name="com.enterpriseandroid.androidSecurity.DEMO_ACTION"/>
</intent-filter>
The core Android platform defines a set of actions that start with android.intent.action, such as android.intent.action.MAIN or android.intent.action.WEB_SEARCH. You can also define custom intent actions as follows:
<action android:name="com.enterpriseandroid.androidSecurity.DEMO_ACTION"/>
Although intent filters are convenient, application developers should not rely on them as a “securely hardened” API. You should also filter on permission access, as explained in the next section, and should sanitize intent arguments per the domain logic of your application.
Your code needs to detect bad arguments and reject those that compromise user security. You can start by looking at the intent methods that provide data to your application, including the following:
getBundleExtra, getCharArrayExtra, getIntExtra
Then make sure you properly sanitize information from these calls, especially if you need to pass intent arguments to sensitive system calls that your application has permission to access. Android has provided a convenient but underused method called Context.checkCallingPermissions for making sure that the call has sufficient privileges to use your application’s privileges.
Listing 12-5 demonstrates how to check the calling permissions for intents that seek to take a picture, access the Internet, and load a URI.
LISTING 12-5: Ensures that calling code has permission to access URI permissions for INTERNET and CAMERA
1 @Override
2 protected void onCreate(Bundle savedInstanceState) {
3 Intent activityIntent = getIntent();
4 String uriParameter =
5 activityIntent.getStringExtra(URI_PARAMETER);
6
7 Uri uriParam = Uri.parse(uriParameter);
8 int checkCallingUriPermissions =
9 checkCallingUriPermission(uriParam,
10 Intent.FLAG_GRANT_READ_URI_PERMISSION);
11 checkGranted(checkCallingUriPermissions,
12 "Uri: " + uriParam.toString());
13
14 String cameraPermission = "android.permission.CAMERA";
15 int checkCameraPermission = checkCallingPermission(cameraPermission);
16 checkGranted(checkCameraPermission, cameraPermission);
17
18 String internetPermission = "android.permission.INTERNET";
19 int checkUriPermission = checkCallingPermission(internetPermission);
20 checkGranted(checkUriPermission, internetPermission);
21 }
22
23 private void checkGranted(int checkPermission, String mesg) {
24 if (checkPermission ==
25 PackageManager.PERMISSION_GRANTED) {
26 Log.d(LOG_TAG, "Permission Granted: " + mesg);
27 } else if (checkPermission ==
28 PackageManager.PERMISSION_DENIED) {
29 Log.d(LOG_TAG, "Permission Denied: " + mesg);
30 }
31 }
The code in Listing 12-5, verifies that the code that launched the given activity has:
So what are the consequences of not using these simple checks? A group of researchers at MIT created a rigorous review of a large number of applications and looked at how they leak privileges using intents. The researchers found that most applications did check for malicious input, but a few did not. As we have mentioned, most intent-related attacks try to get the target application to perform a sensitive operation using permissions it has that the calling code does not. The Android INTERNET permission was the most commonly leaked privilege, where a calling app that does not have the INTERNET permission can pass a URL to load as an intent parameter and direct another app to visit a malicious web page. Unfortunately, the researchers also found that relatively few app developers checked caller permissions. You can learn more about this study as follows:
http://css.csail.mit.edu/6.858/2012/projects/ocderby-dennisw-kcasteel.pdf
As shown previously, several Android components can receive intents, activities included. However, activities also have a convenient attribute, called android:permission, for ensuring that callers have permission to start a given activity. This attribute obviates some of the need shown previously for writing your own intent checks to ensure the caller has a given permission, but will not perform more detailed inspections of intent parameters, such as URIs. The previous code example shows how to protect intents sent to an activity.
The Android broadcast API supports delivery of an intent to multiple receivers in different applications. An Android component sending a broadcast does not see a list of all receivers of the intent, nor does a receiver have knowledge of what code might be sending it intents. Consequently, filtering of intents gains significant importance, as does ensuring that receivers have permission to receive and senders have permission to send. Fortunately, Android supports ways of enforcing security for both ends of broadcast intent delivery.
Broadcast receivers can receive intents for which they did not register, which means developers should be vigilant in filtering their inputs, and only accept broadcast actions that they are designed to accept, as discussed previously. Like the activity tag, the <receiver> tag also supports the android:permission attribute, but it’s still advised that you perform detailed caller permission checking on receiving a broadcast intent.
The following example broadcast receiver requires that all senders have the RECEIVE_BROADCAST permission. Additionally, the receiver only accepts intents with action DEMO_ACTION:
<receiver android:name=".AndroidSecurityBroadcastReceiver"
android:permission=
"com.enterpriseandroid.androidSecurity.permission.SEND_BROADCAST">
<intent-filter>
<action android:name=
"com.enterpriseandroid.androidSecurity.DEMO_ACTION"/>
</intent-filter>
</receiver>
It’s straightforward to send a broadcast intent and require that a receiver have a particular permission to receive, in this case, RECEIVE_BROADCAST:
Intent secureIntent = new Intent("RECEIVE_BROADCAST");
String receivePermission =
"com.enterpriseandroid.androidSecurity.RECEIVE_BROADCAST";
sendBroadcast(secureIntent, receivePermission);
Note that receivers that register for the given action, but do not have the specified permission, will simply not receive the message. Android does not throw a security exception, but instead just blocks the receivers from getting the message.
Android can limit the processes that have the ability to bind or interact with a given service component, by again requiring permission for these operations. The android:permission attribute supports this restriction as follows:
<service
android:name="SecurityDemo"
android:permission="com.enterpriseandroid.androidSecurity.SERVICE_BIND"
android:exported="true"
android:enabled="true"
>
<intent-filter>
...
</intent-filter>
</service>
where you can simply add the permission required for binding to the service declaration. Service operations for starting and stopping all require the specified permission as well.
As you’ve seen in earlier chapters of this book, content providers should be a central part of the data management strategy for your application.
Android provides read and write permissions to limit content provider access to only privileged clients. As you’ve seen, it’s a good idea to use the principle of least privilege to write applications that request read or write access as they need it (for example, to not request read and write, if they only need read). Application developers should also keep in mind that write access does not imply read access. Consider the sync adapter pattern from earlier chapters that reads data from a backend service and pushes it into a content provider, but does not need to read the data it writes; actually the Migrate sync adapter works along these lines. Listing 12-6 shows a provider declaration that requires read and write permissions.
LISTING 12-6: Content provider declaration that requires read and write permissions
<provider
android:name="com.enterpriseandroid.contacts.ContactsProvider"
android:authorities="com.enterpriseandroid.contacts"
android:readPermission="com.enterpriseandroid.contacts.READ_CONTACTS"
android:writePermission="com.enterpriseandroid.contacts.WRITE_CONTACTS"
>
</provider>
When a user has granted permissions as needed to an app, it can invoke content resolver or content provider methods to query, insert, update, and delete contacts. An application that invokes a query but does not have the READ_CONTACTS permission will cause the platform to throw a SecurityException, and likewise the same will happen for a caller that does not have WRITE_CONTACTS but still calls insert, update, or delete.
Read and write access is the minimum control you need to protect content provider data. URI permissions provide an important fine-grained provider access control. They enable content providers to limit access to data for specified namespaces. Consider that the contacts content provider might have grouped contacts into business and personal categories. The following URIs reflect this organization:
content://com.enterpriseandroid.contacts/business
content://com.enterpriseandroid.contacts/personal
When applications need to share subsets of data, Android does something clever; it enables a dynamic permission grant from one application that already has permission to read or write to temporarily give a specified subset of these same permissions to another application. Imagine a contacts application that needs to share a contact with a vCard application. Assuming the contacts application has full access to the contacts provider when it launches an intent to be handled by the vCard application, it can specify in the intent that the vCard application should also have permission to read the data it needs to display a virtual card. Listing 12-7 shows a provider declaration that enables this type of dynamic grant for the contacts content provider.
LISTING 12-7: A provider declaration that allows dynamic grants to the business and personal categories
<provider
android:name="com.enterpriseandroid.contacts.ContactsProvider"
android:authorities="com.enterpriseandroid.contacts"
android:readPermission="com.enterpriseandroid.contacts.READ_CONTACTS"
android:writePermission="com.enterpriseandroid.contacts.WRITE_CONTACTS"
>
<grant-uri-permission android:pathPrefix="/business/"/>
<grant-uri-permission android:pathPrefix="/personal/"/>
</provider>
The code to launch an intent that grants a dynamic permission consists of creating the intent, setting a data URI, and then setting Intent.FLAG_GRANT_READ_URI_PERMISSION, which causes Android to grant the permission to the receiving application. See Listing 12-8.
LISTING 12-8: Code that launches an intent to show a vCard
Uri contactUri =
Uri.parse("content://com.enterpriseandroid.contacts/business/1");
Intent vcardIntent = new Intent(Intent.ACTION_VIEW);
vcardIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
vcardIntent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
vcardIntent.setDataAndType(contactUri, "image/png");
startActivity(vcardIntent);
Figure 12-2 illustrates the sharing of dynamic permission grants:
Note that Android supports wildcarding in provider grant URI declarations. The following example shows an intent that can grant all categories starting with personal, or business followed by a single character:
<grant-uri-permission android:pathPrefix="/personal.*/"/>
<grant-uri-permission android:pathPrefix="/business.*/"/>
In this case, "." means match any single character and "*" means match one or more occurrences of the preceding character.
As this chapter has shown, most forms of data on a mobile device present lucrative hacking targets, worthy of encryption and careful handling. Sending this information over whatever Internet connection your device happens to be using has serious security implications. Who knows whether or not a hacker is peering at your application traffic using Wireshark on WiFi at the nearest cafe?
When a client opens a secure connection, a public key handshake takes place that enables an Android client and backend service to communicate over an encrypted communication stream. As you saw in the discussion of public key encryption, the public key enables encryption by all, but decryption only by the holder of the private key. Unfortunately, a guarantee of encrypted communication does not mean a client can be certain of the identity of the service to which it is connected since an unknown public key does not have any distinguishing features by itself.
To enable clients to authenticate the service, all HTTPS sessions make use of digital certificates, which provide a way to ensure that a client can trust a public key for a given service host. As part of the secure handshake, the service will send its certificate to the client. Secure certificates work on a model of transitive trust, whereby a certificate authority (CA) issues certificates that individual websites distribute. Most OS platforms hold a system keystore that contains well known certificate authorities like VeriSign, Google, and so on. When a client downloads a site certificate, it can verify that an already known certificate authority digitally “vouches” for the new certificate.
The hostname of a web service is encoded into each service certificate, which means that the client can extract the name encoded in the certificate, compare it to the DNS name to which it opened its connection, and make sure that they are the same. The combination of hostname and CA validation allows the Android client to trust the identity of the target service.
As noted previously, web browsers and Android applications secure Internet traffic using HTTPS, which encrypts data sent between a client and web server and eliminates the risk of a “man in the middle” attack. It’s not computationally feasible for an eavesdropper on the network to read the communication. In this code example, you’ll invoke a secure request on the Chapter 6 $CODE/springServiceContacts service. You’ll make this request using HTTPS by setting up secure communication on the client and backend service, with the following steps involved:
The task of securing a backend web service for an Android application can be quite easy, but securing services with a moderate level of complexity provides significant challenges as hackers find clever ways to get past your defenses. This chapter only covers the straightforward steps involved in securing the RESTful web services from Chapter 6, as follows:
The next few sections walk through implementing these tasks using the code in $CODE/springServiceContacts.
Before buying or signing your own certificate, you will need to create a “Certificate Signing Request” that you can send to a commercial CA, or sign yourself (with instructions that follow). You can learn more information about this request online:
http://en.wikipedia.org/wiki/Certificate_signing_request
The following steps for creating this request are based on the OpenSSL tool, which is pre-installed on Linux and Mac OS:
/usr/bin/openssl
cd $CA_DIR
/usr/bin/openssl genrsa -out contacts_privkey.pem 1024
/usr/bin/openssl req -new -x509 -key contacts_privkey.pem -out
contacts_cert.pem
cat contacts_cert.pem contacts_privkey.pem |/usr/bin/openssl x509
-x509toreq -signkey contacts_privkey.pem -out contacts_certreq.csr
Now that you have a signing request, you can choose to do one of two things with it:
As previously discussed, a service needs a certificate for secure handshakes with clients. It turns out that with mobile applications it’s entirely possible to become your own certificate authority and avoid any hassle associated with buying certificates. You can create as many of your own free and valid certificates as you need. If you want to become your own certificate authority, run the following utilities, as per your platform:
/usr/lib/ssl/misc/CA.pl –newca
/System/Library/OpenSSL/misc/CA.pl -newca
perl /usr/ssl/misc/CA.pl –newca
Don’t skip any field, record what you enter, and re-use your answers in future steps.
When the program completes, you should have the files you need to act as your own CA. Your CA certificate should now reside in demoCA/cacert.pem, and you can start signing certificates with it. Note that the default expiration period of certificates that you sign with your CA is 365 days, as specified in the CA.pl script. If you need to make the time period longer, you’ll need to modify CA.pl to change the duration.
Now sign the certificate request from Step 6 of “Creating a ‘Certificate Signing Request’” with the files generated in demoCA. Run the following command using the output, demoCA directory from “Creating your own CA” (note that the command “knows” the directory name, demoCA):
/usr/bin/openssl ca -policy policy_anything -in
contacts_certreq.csr -out contactservice.pem
Now that you have a certificate, you can start the process of setting up Tomcat to use it and exporting the contacts service with an HTTPS transport. You can configure Tomcat to use your certificates in two ways:
openssl pkcs8 -topk8 -nocrypt -in contacts_privkey.pem -inform PEM -out
contacts_privkey.der -outform DER
openssl x509 -in contactservice.pem -inform PEM -out contactservice.der
-outform DER
$CODE/AndroidSecurity/ImportKey.java
javac –d . ../ImportKey.java
java -Dkeystore=<cadir>/tomcat_keystore.jks ImportKey <cadir>/contacts_
privkey.der <cadir>/contactservice.der
keytool -list -v -keystore tomcat_keystore.jks
<Connector port="8443" maxThreads="200"
scheme="https" secure="true" SSLEnabled="true"
keystoreFile="<ca_dir>/tomcat_keystore.jks" keystorePass="<your_password>"
clientAuth="false" sslProtocol="TLS"/>
APR is significantly easier to configure than JSSE. You can just use the certificates you have created by directly configuring them into the Tomcat configuration file, $CATALINA_HOME/conf/server.xml, as follows:
<-- Define a SSL Coyote HTTP/1.1 Connector on port 8443 -->
<!--
<Connector
port="8443" maxThreads="200"
scheme="https" secure="true" SSLEnabled="true"
SSLCertificateFile="<ca_dir>/contactservice.pem"
SSLCertificateKeyFile="<ca_dir>/contacts_privkey.pem"
clientAuth="optional" SSLProtocol="TLSv1"/>
-->
Once you have edited server.xml, you’ll need to complete one last task: Edit $CODE/springServiceContacts/applicationContext-rest.xml and uncomment the following line:
<!--<security:http-basic/>-->
as documented in the file. Then rebuild springServiceContacts and deploy the project war to Tomcat. Now that you have a secure backend, it’s time to move on to creating a secure connection to it from Android.
As noted in Chapter 5, Android supports a number of ways to open a secure connection. Listing 12-9 demonstrates how to do so using the Apache framework, but you could also use the Spring framework for Android. The example shows that it’s a pretty simple operation; you just need to create a connection manager and socket factories for each of the ports you will serve — a secure one for HTTPS and a plain text factory for unencrypted port 80. This example allows you to use the certificates signed using a commercial certificate and your own root CA.
LISTING 12-9: Secure connection using the Apache framework
package com.enterpriseandroid.androidSecurity;
import java.io.IOException;
import java.io.InputStream;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import org.apache.http.client.HttpClient;
import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.conn.scheme.PlainSocketFactory;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpParams;
import android.content.res.Resources;
/**
* Creates an Https client that loads a keystore that can supply an
* application defined root certificate authority to validate the client
* connection.
*
* This client can also authenticate using the system keystore which
* contains standard CAs as well.
*/
public class HttpsClientHelper {
public static HttpClient getHttpClient(Resources resources)
throws KeyManagementException, UnrecoverableKeyException,
NoSuchAlgorithmException,
KeyStoreException, CertificateException,
IOException
{
If you created “your own CA,” the following code provides the implementation of the method for avoiding paying for a valid certificate. The code loads your own root CA from a trusted keystore stored in a raw file.
Note though that the format of the keystore must not be “JKS” as used with Tomcat. It needs to be, “BKS” — for Bouncy Castle Keystore, which is the only format that Android supports. To use your own CA certificates, you will need to import the file <cadir>/demoCA/cacert.pem into a BKS keystore.
You will need to use a utility to import the converted certificate files into the keystore. Download the following useful graphical tool, called Portecle, for this task:
http://sourceforge.net/projects/portecle/
Unzip the download file and then run Portecle using the following command line from the unzipped directory:
java –jar <unzip_dir>/portecle.jar
Using the portecle UI, import your CA certificate into a new keystore, which should be of type BKS, and save it into the following directory with the name listed as follows:
$CODE/AndroidSecurity/res/raw/your_ownca_keystore.bks
Record the password for the keystore. For convenience, you can use the same password you used previously.
When asked, confirm that the certificate is trusted and accept the alias (for example, my root ca); ignore the previous instructions, and comment out the lines to load the BKS keystore.
KeyStore localRootStore = KeyStore.getInstance("BKS");
// Contains your application's root CA and allows use of
// certificates that you sign with that CA.
InputStream in = resources.openRawResource(R.raw.your_own_ca_keystore);
localRootStore.load(in, "changeit".toCharArray());
A scheme registry will hold the socket factories for port 80 and 443, non-secure and secure.
// Use unencrypted factory for http port 80
SchemeRegistry schemeRegistry = new SchemeRegistry();
schemeRegistry.register(new Scheme("http",
PlainSocketFactory.getSocketFactory(), 80));
// Use a secure socket factory for 443, but this socket
// factory will consider our "root" trust store when
// making its connection.
SSLSocketFactory sslSocketFactory =
new SSLSocketFactory(localRootStore);
schemeRegistry.register(new Scheme("https",
sslSocketFactory, 443));
HttpParams params = new BasicHttpParams();
ClientConnectionManager cm =
new ThreadSafeClientConnManager(params,
schemeRegistry);
HttpClient client = new DefaultHttpClient(cm, params);
return client;
}
}
This code configures an HTTP client that can connect to the Chapter 6 service as follows:
Just like the service’s site certificate enables the client to trust the host to which it is connecting, the client also needs to authorize its user to the service. Modern mobile and web applications use two standard modes of authentication — HTTP basic authentication and a protocol called OAuth. HTTP basic authentication is by far the most common mechanism for authenticating users to backend services. However, due to greater flexibility, OAuth is quickly replacing HTTP basic as the de facto authorization for accessing service resources.
The various RESTful APIs for Android all directly support HTTP basic. To provide a bit more detail, HTTP basic makes use of a header and a base-64 encoded username:password to transmit credentials in every RESTful invocation of a secure backend service. When using HTTP basic, applications don’t so much as log in to a service, as they simply collect user credentials that can be attached to authorize every RESTful request. Readers familiar with JavaScript will recognize the code used to add basic authentication to an AJAX request:
import base64
encodedAuth = base64.encodestring('%s:%s' % (username, password))[:-1]
req.add_header("Authorization", "Basic %s" % encodedAuth)
The resulting header looks like the following:
Authorization: Basic FJkjuekjDFJskjDKlFJSksfspt==
Listing 12-10 finishes the secure invocation with an example snippet of how to do basic authentication in Java for Android.
LISTING 12-10: HTTP basic authentication in Java for Android
UsernamePasswordCredentials credentials =
new UsernamePasswordCredentials(user, pass);
The code creates a new instance of the HTTPS client utility helper.
HttpClient httpClient =
HttpsClientHelper.getHttpClient(mContext.getResources());
It then creates a new AuthScope that holds the basic credentials:
AuthScope as = new AuthScope(host, 443);
((AbstractHttpClient) httpClient).getCredentialsProvider()
.setCredentials(as, credentials);
It then creates a new basic contact and sets the basic auth header:
BasicHttpContext localContext = new BasicHttpContext();
BasicScheme basicAuth = new BasicScheme();
localContext.setAttribute("preemptive-auth", basicAuth);
It then creates a new HTTP request and executes the request with attached credentials to authenticate the client:
HttpHost httpPost = new HttpHost(host, 443);
HttpResponse response =
httpClient.execute(httpPost, getRequest);
response.getStatusLine();
Now that you’ve seen all relevant concepts, you can run the example:
Launch your securely modified springServiceContacts using the configuration changes listed previously.
Make sure to enter the hostname from Step 5 in “Creating a ‘Certificate Signing Request’” into the following field:
SecureConnectionActivity.SECURE_HOST
Additionally, set the password to your BKS keystore in HttpsClientHelper.CAPASSWORD.
Then run the SecureConnectionActivity in $CODE/AndroidSecurity project (choose the secure connection option).
Although basic authentication may be common, superior forms of authentication have come into common usage on the Internet. Specifically, OAuth allows clients to access server resources without the liability of using passwords, much like the Android account manager. Clients access user data on behalf of a user, and access a set of resources available under an OAuth ID, which is a one-time temporary identifier that cannot leak the same way as a password. This version of Enterprise Android does not provide a code example for OAuth, but you can learn more about it in relation to Android at the following URL:
http://developer.android.com/training/id-auth/authenticate.html
Android APIs greatly simplify the task of managing passwords with the inclusion of the android.accounts.AccountManager API, which allows an application to list service accounts registered on the system (for Google, Facebook, and MS Exchange, and of course, the Migrate sync account from Chapter 10).
Applications can manage accounts, and most importantly can obtain authorization tokens from them in the style of OAuth. Specifically, the account manager precludes the need for the application to store passwords to remote services.
The URL referenced in the previous section leads to explanations in detail of how the account manager works with OAuth.
This section contains a simple exploration of how the Android account manager works. The chapter includes code to access the account manager, to list its accounts, and to obtain an auth token from one account.
The code lists Android accounts, looking in particular for the Migrate account, and then acquires an auth token from it for display. In a real usage, the auth token could be used for authorization to access service resources. See Listing 12-11.
LISTING 12-11: A simple demonstration of the Android account manager
package com.enterpriseandroid.androidSecurity;
import android.accounts.Account;
import android.accounts.AccountManager;
import android.accounts.AccountManagerCallback;
import android.accounts.AccountManagerFuture;
import android.accounts.AuthenticatorException;
import android.accounts.OperationCanceledException;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
/**
* Use the Android account manger to lists accounts and get
* an auth token from the Migrate account setup for Chapter 10.
*/
public class AuthTokenActivity extends Activity{
/** The tag used to log to adb console. **/
private static final String TAG = "AuthTokenActivity";
private static final String ACCOUNT_TYPE="myAccountType";
private AccountManager mAccountManager = null;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.auth_token_activity);
final Bundle bundle = savedInstanceState;
try {
mAccountManager = AccountManager.get(this);
Access and display the Android accounts:
Account [] accounts =
mAccountManager.getAccounts();
String accountsList =
"Accounts: " + accounts.length + "
";
for (Account account : accounts) {
accountsList += account.toString() + "
";
}
setText(R.id.message, accountsList);
} catch (Exception e) {
setText(R.id.message, e.toString());
}
Button loginBtn = (Button)
findViewById(R.id.login);
loginBtn.setOnClickListener( new OnClickListener() {
public void onClick(View v) {
try {
Account [] accounts =
mAccountManager.getAccounts();
if (accounts.length == 0) {
setText(R.id.result, "No Accounts");
return;
}
Account account = accounts[0];
Obtain the auth token for the Migrate account:
mAccountManager.getAuthToken(account,
"com.migrate.webdata.account", bundle,
false, new
accountManagerCallback(), null);
} catch (Exception e) {
setText(R.id.result, e.toString());
}
}
});
}
private class accountManagerCallback implements
AccountManagerCallback<Bundle>
{
public void run(AccountManagerFuture<Bundle> result) {
Bundle bundle;
try {
bundle = result.getResult();
Intent intent =
(Intent) bundle.get(AccountManager.
KEY_INTENT);
if(intent != null) {
// asked user for input
startActivity(intent);
} else {
setText(R.id.result, "auth token: " +
bundle.getString(AccountManager.KEY_AUTHTOKEN));
}
} catch (Exception e) {
Log.e("TAG", "accountManagerCallback failed: " + e);
setText(R.id.result, e.toString());
}
}
};
public void setText(int id, String msg) {
TextView tv = (TextView) this.findViewById(id);
tv.setText(msg);
}
}
Now that you can secure and authenticate both ends of a secure invocation between an Android client and a backend service you’re well on your way to writing secure enterprise Android applications that you can deploy to a variety of major cloud vendors. Now it’s time to shift focus to how to protect your Android applications from theft.
Android is designed to be an open platform on which users can install applications from different vendors. Pretty much anyone can create and install an application for Android, with few limitations. It’s even possible to download and install entirely new application stores. This flexibility afforded to users does not come without cost to application developers.
Recall the earlier discussion regarding keeping malware off user devices that noted how Android supports several sources from where users can install applications onto Android devices. These sources include the Google Play and Amazon app stores, installing from unknown sources on the Internet, and side loading from SD cards. In contrast, with iOS, Apple allows application installation only from the Apple App store (not counting “jail-broken” devices). Also consider that the Internet has no shortage of industrious hackers eager to break or pirate successful applications so that they can be installed and used free of charge.
The bottom line is Android allows easy installation of pirated applications, as long as users are willing to risk malware infection, assuming they consider the possibility at all. Indeed, many application developers have voiced concerns regarding the ease with which users can install pirated applications. As an example, the first person shooter game “Dead Trigger” dropped its price from $0.99 to free due to extremely widespread piracy:
http://www.androidcentral.com/how-high-unbelievably-high-piracy-dead-trigger-devs-not-saying
Some estimates state the revenue for Android applications is as much as 40 percent less than the same or comparable applications on iOS.
The Google Play licensing service is one way to protect revenue from your Android applications from piracy. The Google Play app store provides a license verification service that verifies whether the current user has a valid license. The application can decide to shut the app down or provide appropriate behavior when a user attempts to run an app without a license. You can find out more about the licensing service here:
http://developer.android.com/google/play/licensing/index.html
This chapter began by covering the steps that handset users can take to keep malicious applications off their phones. This coverage included a walkthrough of tools from Google and a few other vendors, as well as security reviews of these utilities; it turns out that there is still quite a bit of risk involved when users install applications from unknown vendors. The chapter then moved to present results from the malware genome project and discussed types of security attacks for which application developers should build defenses in their applications. The chapter included an introduction to Android permissions, and promoted an understanding of those permissions as informed by the results of the malware genome project. The chapter directed developers to consider typical application weaknesses and to understand the ingredients of applications that could protect them, which included:
The chapter included practical demonstrations of these security tenants in the form of:
In conclusion, application developers should keep in mind that while Google and other developers are constantly improving malware defenses, malware is also in a race to circumvent safeguards to profit from theft and invasion of user privacy. Developers should consider how users will protect their devices, write their applications to defend against attacks, and beware the significant consequences of lapses in security. To avoid security holes, follow the precautions discussed in this chapter and in the Android developer documentation and stay current with the latest in Android security news.