Chapter     6

Security

In this chapter we will explore recommendations for secure Android development and coding, from a range of industry sources. These different security recommendations represent the best current thinking on the topic, and I’ve added my own additional measures gathered from hard-earned experience building and deploying leading Android applications.

The State of Android Security

There have probably been more books, blog postings, and magazine articles written on the topic of Android security than on any other mobile platform. Whether we like it or not, Android is seen as the Wild West of the mobile world. Because all iOS apps are reviewed by a human, rightly or wrongly this gives people the perception that iOS apps are safer than Android apps. But how can that be? After all, the Android platform does a pretty good job of separating APKs so that each one runs in its own sandbox? Let’s take a look at some empirical data to see if there is any truth in the rumor. Figure 6-1 shows a Secure List report, available at http://www.securelist.com/en/analysis/204792239/IT_Threat_Evolution_Q2_2012.

9781430258575_Fig06-01.jpg

Figure 6-1. The number of malware modifications targeting Android OS

You can see that the number of malware apps in the Android space is indeed growing dramatically. The report goes on to say that among the 15k apps, malware characteristics were found as listed in Table 6-1.

Table 6-1. Breakdown of the Types of Malware

Percentage

Malware Type

49%

Steal data from telephones

25%

SMS messaging

18%

Backdoor apps

2%

Spy programs

There have also been some famous fake apps, like the phony Netflix app (see http://www.symantec.com/connect/blogs/will-your-next-tv-manual-ask-you-run-scan-instead-adjusting-antenna), which looked like the Netflix app and simply collected usernames. And almost every famous Android app from Angry Birds to the Amazon App Store app itself has a suspicious clone that hopes to dupe a customer into downloading it for a fee. And yes, this aspect of security should really be a two-way street; I’m sure that users pay little or no attention to that permission screen when they’re installing an APK and will typically approve anything. So while it seems that we do have a problem on the Android platform, perhaps it’s not all the developer’s fault.

Back in the Cupcake and Donut eras there were few if any checks. But now we can say that every developer needs to have a credit card to upload an app. Since around the time of Gingerbread, Google Bouncer has also automatically checked to see if the app has any malware or Trojans installed on your APKs, so it should be much safer. (However, Jon Oberheide’s paper describing how he created a fake developer account and bypassed the Google Bouncer, at http://jon.oberheide.org/files/summercon12-bouncer.pdf, is some cause for concern about the effectiveness of the Google signup process.) Things are definitely getting more secure as more users move to Ice Cream Sandwich and Jelly Bean; at the time of writing, 40% of Android devices hitting Google Play were on some version of 4.x.

But perception is reality, and even if most of these hacks are becoming a thing of the past, Android is still seen as a less secure platform than iOS. So what can a developer do? You can make sure that your APKs are secure as possible to help change that Wild West perception. This chapter will show how to ensure that your APKs do what your users expect—no more and no less, in a consistent way.

There are a number of best practices that you can adopt to make your Android apps more secure. In this chapter I’ll provide you with a better understanding of what it takes to create a trustworthy app; the goal is that if someone downloads your app, they can be safe in thinking that it’s not going to cause them any security problems.

The bulk of this chapter compiles a top 10 list of secure coding practices. We’ll first look at some industry standard lists and merge them into our own best-practices top 10 list. This isn’t really meant to be a definitive list; it’s simply a list of the most important issues from personal experience, research, and a couple of industry-standard lists.

It also makes sense to look at an Android app that my company, RIIS, uses to teach our developers how to write secure code and talk you through how we go about that.

Secure Coding Practices

Your APKs should use a least-privileges concept so that they always get access only to the privileges they really need, and are not being granted other privileges that are never used but could open vulnerabilities. So exactly how do you make sure of that?

If you’re a consumer, there are a variety of tools that check permissions, but if you’re a developer or a manager, there are a very limited number of tools out there.

Once your APK is out on Google Play, phones can be rooted, and the APK can be very easily reverse-engineered to see any usernames/passwords or other login info. It’s in everyone’s interest to make sure that a customer’s data is not in plain text so it can be compromised. We’ve seen some really strange method names when decompiling an APK, one of my favorites being updateSh*t, which probably isn’t something you want out there with your company’s name attached.

You may also want to get a better feel for whatever third-party libraries you’re using and make sure they’re not doing anything they shouldn’t be; for example, AdMob makes location requests for collecting marketing information. You might want to know if the third-party APK has hardcoded usernames and passwords, too, and what they might be doing.

To solve this problem, I came up with my top 10 list of Secure Coding practices. Most of them came from looking at other security lists that smarter people than I have developed.

This list becomes my firm’s barometer on what’s acceptable and not acceptable in an Android APK that we develop. That’s not to say that some APKs won’t violate one or more of the guidelines in the top 10 list for perfectly good reasons, but it raises a red flag so that someone can ask why it’s doing something that we didn’t expect it to do.

These are not the type of issues that Google Bouncer would be checking; this is code that shouldn’t be in your APK—in our humble opinion—without a good reason.

Industry Standard Lists

Before we come up with our own list, let’s take a look at the following security lists:

  • PCI’s Mobile Payment Acceptance Security Guidelines
  • OWASP or Open Web Application Security Project’s top 10 mobile controls and design principles
  • Google’s Security Tips

PCI List

In September 2012, The PCI Security Standards Council released v1.0 of the Mobile Payment Security Guidelines. PCI’s focus is on payment processing, and while the guidelines are not yet mandatory, they are an excellent place to start. Some of the items in the PCI guidelines don’t directly apply to mobile developers, but there are some that are crucial, which we’ve included here.

  • Prevent account data from compromise while processed or stored within the mobile device. Android developers should ensure that all data is securely stored and minimize any chance of data leakage. Storing sensitive customer information unencrypted in SQLite or in a file on an SD card is not acceptable. The safest option is to not store cryptographic keys anywhere on the mobile phone if possible, but if that’s not an option, then the keys need to be stored securely so that they are not accessible even when a phone is rooted.
  • Prevent account data from being intercepted upon transmission out of the mobile device. Any sensitive customer information, in this case payment information, should be transmitted securely using SSL and not sent in clear text.
  • Create server-side controls and report unauthorized access. Report on unauthorized access over a given threshold via server side logging messages, updates to software, phone rooting, and so on.
  • Prevent escalation of privileges and remotely disable payments. If the user roots their phone, the application should report the change and provide the ability to stop taking payments if necessary.
  • Prefer online transactions. Transactions should be taken when the phone is online and not saved for later processing if the phone is offline for any reason. Storing the payment data increases the risk of a hacker gaining access to the payment data.
  • Conform to secure coding, engineering, and testing. There are a number of Android specific coding techniques, such as avoiding the use of MODE_WORLD_WRITABLE or MODE_WORLD_READABLE when writing to files, that the developer should know. In the remainder of the chapter we’re going to look at what secure coding means for an Android developer.
  • Support secure merchant receipts. Any receipt-type messages, whether they are displayed on screen or sent via email, should always mask the credit card number and never display the complete number.
  • Provide an indication of a secure state. Unfortunately, unlike web browsers, Android apps don’t have the concept of a locked and unlocked padlock to show the user that any payment information is being sent securely, so currently there is no way to indicate a secure or insecure state.

OWASP

OWASP, the Open Web Application Security Project, is aimed at providing information to developers so that they can write and maintain secure software. No longer only for the web, OWASP also provides information on secure cloud programming as well as secure mobile programming. OWASP together with ENISA (the European Network and Information Security Agency) published the Top Ten Mobile Controls as shown following. This list is aimed at mobile device security, rather than just payment security. OWASP also provide another resource called GoatDroid, which consists of a couple of Android applications showing examples of insecure code that does not follow the advice on the list.

  • Identify and protect sensitive data on the mobile device. Mobile phones have a higher risk than laptops of being stolen. Store any sensitive user data on the server side and not on the mobile device. If you do need to store data on the mobile device, then encrypt the data and provide a way to remotely remove the key or data so that the user can wipe the information if the phone is stolen. Consider restricting access to the data or functionality using the phone’s location, for example, if the phone is no longer in the same state, province, or country where the app was first installed. Practice secure key management.
  • Handle password credentials securely on the device. Store passwords on the server. If they do need to be stored on the phone, never store passwords in clear text; use encryption or hashing. If possible use tokens, for example OAuth, instead of passwords where possible and make sure they expire. Make sure passwords are never visible in logs. Do not store any keys or passwords, for example to back-end servers, in the application binary, as mobile apps can be reverse-engineered.
  • Ensure sensitive data is protected in transit. Use SSL/TLS when sending any sensitive information to back-end systems. Use strong and well known encryption with appropriate key lengths when encrypting data. User passwords are often too short to provide adequate key lengths. Use trusted Certificate Authorities or CAs. Do not disable or ignore SSL certs if the trusted CA is not recognized by the Android OS. Do not use SMS or MMS to send sensitive user information. Let the end user know, by using some visual indicator, that the CA is valid.
  • Implement user authentication, authorization, and session management correctly. Use unpredictable seeds and a random number generator for key generation. Instead of just using date and time, use other inputs such as phone temperature, current location, and so on. When the user is logged in, make sure that any further requests to the back-end server still require the same login credentials or token to get the information.
  • Keep the back-end APIs (services) and the platform (server) secure. Test your back-end servers and APIs for any vulnerabilities. Apply the latest OS patches and updates to the server. Log all requests and check to see if there is any unusual activity. Use DDOS limiting techniques such as IP/per-user throttling.
  • Secure data integration with third party services and applications. There is so much open source Android code available that sometimes coding an app can seem more plug and play than desktop programming. However, third-party libraries also need to be checked for insecure coding practices. Apply the same checks to your third party code as you would to your own code. Don’t assume that commercial apps are going to be secure. There are plenty of examples of third party issues, such as advertising networks collecting location and device information. Check for software patches and update your mobile applications as needed.
  • Pay specific attention to the collection and storage of consent for the collection and use of the user’s data. Ask for consent before asking for and storing a user’s personally identifiable information. Allow the end user to opt out. Perform audits to ensure that you are not leaking any unintended information, for example in image metadata. Be aware that the data collection rules may be different in different regions; for example, user consent is mandatory for any personal data collection in the European Union.
  • Implement controls to prevent unauthorized access to paid-for resources (wallet, SMS, phone calls, and so on.) In the PCI list introduced earlier in this chapter, we saw that many of the malware apps wreak havoc by using costly paid-for resources such as SMS messaging to offshore numbers. To prevent your app from being hijacked in a similar fashion, there are certain steps that you should take if you use paid-for resources in your mobile app.
  • Track any significant changes in usage or a user’s location and notify the user or shut down the app. Authenticate all API calls to paid for resources and warn the user for any paid for access. Finally, maintain logs of any paid-for access API calls. Audit the logs, as they may alert you to any changes in overall behavior before you app is compromised and can also help you understand what happened after an attack.
  • Ensure secure distribution/provisioning of mobile applications. Don’t distribute your app through nonsecure mobile app stores, as they may not monitor for insecure code. Provide a security email address (such as [email protected]) for users to report any security problems with your app. Plan your security update process. Remember that many users will not automatically accept the latest update. So if you have a security flaw, it may take many months before all your users have updated to the latest secure version of your mobile app. And once an APK is out there, if your app has lots of users, then it’s always going to be out there on any number of hacker forums ready for someone to see if they can exploit your flaw.
  • Carefully check any runtime interpretation of code for errors. Test all user inputs and make sure all input parameters are correctly validated and there are no options for either cross-site scripting or SQL injection.

OWASP’s General Secure Coding Guidelines

OWASP also offers more general secure coding guidelines, which apply to mobile programming:

  1. Perform abuse case testing, in addition to use case testing.
  2. Validate all input.
  3. Minimize lines and complexity of code. A useful metric is cyclomatic complexity.
  4. Use safe languages (for example, from buffer-overflow).
  5. Implement a security report handling point (address), such as [email protected].
  6. Use static and binary code analyzers and fuzz-testers to find security flaws.
  7. Use safe string functions, and avoid buffer and integer overflow.
  8. Run apps with the minimum privilege required for the application on the operating system. Be aware of privileges granted by default by APIs and disable them.
  9. Don’t authorize code/app to execute with root/system administrator privilege.
  10. Always perform testing as a standard as well as a privileged user.
  11. Avoid opening application-specific server sockets (listener ports) on the client device. Use the communication mechanisms provided by the OS.
  12. Remove all test code before releasing the application.
  13. Ensure that logging is done appropriately but do not record excessive logs, especially those including sensitive user information.

OWASP’s Top 10 Mobile Risks

OWASP has another top 10, called the Top 10 Mobile Risks. These have a lot of overlap with the earlier Top 10 Mobile Control, which is more of a best practices list. I show the Top 10 Mobile Risks here for completeness.

  1. Insecure data storage
  2. Weak server-side controls
  3. Insufficient transport layer protection
  4. Client-side injection
  5. Poor authorization and authentication
  6. Improper session handling
  7. Security decisions via untrusted inputs
  8. Side channel data leakage
  9. Broken cryptography
  10. Sensitive information disclosure information

Google Security Tips

The last list we’re going to look at is Google’s Android-specific list of security tips. You’ll see some overlap with the earlier lists, but because it’s so specific to our Android requirements, it may very well prove to be the most useful of the three lists.

  • Storing Data: Avoid using MODE_WORLD_WRITEABLE or MODE_WORLD_READABLE modes for files, especially if you’re using the files to store user data. If you do need to share data between applications then use a content provider where there is a much finer degree of control on what applications can access the data. Keys should be placed in a keystore encrypted with a user password that is not stored on the device.
  • Do not store any sensitive user data on external storage such as an SD card. The SD card can be removed and examined, as it’s globally readable and writable.
  • Using Permissions: Android APKs work within a sandbox. The APK can communicate outside of the sandbox by a series of permissions, which the developer requests and the user accepts. Developers should adopt a least-privileges approach to permissions and ask for only the very minimum level of permissions to provide the desired functionality. And if there is an option to not request permissions, such as using internal rather than external storage, then the developer should take steps to define as few permissions as possible.
  • Using Networking: Use SSL instead of sending any sensitive user information in clear text across the network. Do not rely on unauthenticated SMS data to perform commands, as it may have been spoofed.
  • Performing Input Validation: Perform input validation and ensure that there is no SQL or JavaScript script injection. If you are using any native code in your app, then apply C++ secure coding best practices to catch any buffer overflows. These should be taken care of by proper management of buffers and pointers.
  • Handling User Data: The topic of how to handle user data is one that appears time and time again in security lists. Minimize any access to sensitive user data. While it may be necessary to transmit usernames, passwords, and credit card information, the data should not be stored on the device. User data should also be hashed, encrypted, or tokenized on the server so that the data is not transmitted in clear text. User data should also not be written to logs. Use a short-lived authorization token after initial authentication with a username and password entered by the user.
  • Using WebView: Disable JavaScript when using WebView if it’s not required. To reduce the chance of cross-site-scripting, do not call setJavaScriptEnabled() unless you absolutely must, such as when building hybrid native/web applications. By default setJavaScriptEnabled is false.
  • Using Cryptography: Use existing cryptography such as AES and RSA; don’t implement your own cryptographic algorithms. Use a secure random number generator. Store any keys that are needed for repeated use in KeyStore.
  • Using Interprocess Communication:Use Android’s interprocess communication, for examples intents, services and broadcast receivers. Do not use network sockets or shared files.
  • Dynamically Loading Code: Dynamically loading code is strongly discouraged. In particular, loading code from outside of the APK over the network could allow someone to modify the code in transit or from another application and should be avoided.
  • Security in Native Code: Simply put, using the Android NDK is discouraged as C++ is prone to buffer overflows and other memory corruption errors.

Our Top 10 Secure Coding Recommendations

Not content with the existing lists, I’ve come up with my own Top 10 list, which is a mashup of the other lists, where I’ve picked what I feel are the best practices for each of the lists.

I’m also a great believer in automating the analysis wherever possible and not manually checking every app, so I’ve written a secure code analyzer called Secure Policy Enforcer or SPE to ensure that your apps are following the top 10 list.

  • Apply secure coding techniques. There shouldn’t be any need to open a file as WORLD_READABLE or WORLD_WRITEABLE as done in Listing 6-1; the default behavior is not to open a file as WORLD_READABLE or WORLD_WRITEABLE See.

    Listing 6-1.   Insecure technique - opening a file as WORLD_READABLE, WORLD_WRITEABLE

    // Code fragment showing insecure use of file permissions
    FileOutputStream fos;
    try {
       fos = openFileOutput(FILENAME, MODE_WORLD_READABLE |
                                      MODE_WORLD_WRITEABLE);
       fos.write(str.getBytes());
       fos.close();
    } catch (FileNotFoundException e) {
       e.printStackTrace();
    } catch (IOException e) {
       e.printStackTrace();
    }
  • Similarly, opening a database as WORLD_READABLE or WORLD_WRITEABLE shouldn’t be a requirement.
  • Use encrypted SQLite. SQLite is a great place to store information but it’s not a good place to store credit card information. One of the APKs my company looked at stored the credit card number encrypted in SQLite, but it also stored the key unencrypted in another column. If you do use SQLite, then use something like SQLCipher, which takes three lines of code to encrypt the database so it’s harder to find anything. Listing 6-2 shows an unencrypted database connection, which can be encrypted by using Import net.sqlcipher.database.SQLiteDatabase instead of android.database.sqlite.SQLiteDatabase and calling SQLiteDatabase.loadLibs(this) before the database is connected.

    Listing 6-2.   Insecure technique - unencrypted database connection

    public UserDatabase(Context context) {
       super(context, DATABASE_NAME, null, 1);
        
       String CREATE_TABLE = "CREATE TABLE IF NOT EXISTS " + TABLE + " ("
              + KEY_DATE + " INTEGER PRIMARY KEY, "
                + KEY_LOC + " TEXT NOT NULL)";
        db.execSQL(CREATE_TABLE);
    }
  • Reading a SQLite database from a device is relatively straightforward, although the commands are a bit arcane. Using the Android backup command, you first back up the APK’s application data using the following command
    adb backup -f data.ab -noapk com.riis.callcenter-1.apk
  • This exports the data in an Android backup format, which can be extracted using the following command:
    dd if=data.ab bs=1 skip=24 | openssl zlib -d | tar -xvf -

    Note   Using openssl as shown requires your version of openssl to be compiled with zlib support.

  • The SQLite database file can then be opened by an intruder using SQLite Database Browser, shown in Figure 6-2, which displays credit card information in clear text. SQLite Database Browser is available at http://sourceforge.net/projects/sqlitebrowser.

    9781430258575_Fig06-02.jpg

    Figure 6-2. SQLite Database Browser with unencrypted data

  • To avoid this security risk, using SQLCipher encrypts the data so it can no longer be seen, as illustrated in Figure 6-3.

    9781430258575_Fig06-03.jpg

    Figure 6-3. SQLite Database Browser with encypted data

  • Don’t store anything on an SD card. If you’re storing data on an SD card (a real one, not the impersonated style in later versions of ICS, Jelly Bean, or KitKat), then it’s easy for an intruder to read any data externally on a PC or MAC. Unless you have to support very old devices and Android versions that relied on SD cards because of limited internal memory, you could write the data out to a local file or possibly use shared preferences to store any data. Listing 6-3 shows an example of writing to an SD card.

    Lsiting 6-3.   Insecure technique - writing to an SD Card

    private void writeAnExternallyStoredFile() {
        //An example of what not to do, with poor SD card data security
        try {
            File root = Environment.getExternalStorageDirectory();
            if (root.canWrite()){
                File gpxfile = new File(root, "gpxfile.gpx");
                FileWriter gpxwriter = new FileWriter(gpxfile);
                BufferedWriter out = new BufferedWriter(gpxwriter);
                out.write("Hello world");
                out.close();
            }
        } catch (IOException e) {
            Log.e("TAGGYTAG", "Could not write file " + e.getMessage());
        }
    }
  • Avoid unnecessary permissions. Permissions are set in the android_manifest.xml file. If any app is asking for permissions, such as reading contacts, sending texts, recording audio, sending SMS messages, or calling home, you may want to ask yourself if that’s really needed and remove it from the manifest file if it doesn’t affect the functionality of your app. Here’s the list of permissions that are best avoided:
    • ACCESS_COARSE_LOCATION
    • ACCESS_FINE_LOCATION
    • CALL_PHONE
    • CAMERA
    • INTERNET
    • READ_CALENDAR
    • READ_CONTACTS
    • READ_INPUT_STATE
    • READ_SMS
    • RECORD_AUDIO
    • SEND_SMS
    • WRITE_CALENDAR
    • WRITE_CONTACTS
  • Looking for root permissions. Some apps will check for root permissions to make sure the phone is not rooted before it starts, as shown in Listing 6-4. I recommend not checking to see if the device has been rooted. There is rarely a good reason to check. If the APK has been installed on a rooted device, then it’s already at risk of being reverse-engineered; checking to see if the phone is rooted at run time is probably too late.

    Listing 6-4.   Looking for Root Permissions

    try {
       Runtime.getRuntime().exec("su");
       //NOTE! This can cause your device to reboot - take care with this code.
       Runtime.getRuntime().exec("reboot");
    }
  • Limit user data on the device. Many APKs store sensitive user data insecurely for future use. To create a better user experience, they have the user enter their login credentials the very first time they open the app and save it in a file or database for later retrieval. The next time the user opens the app they don’t have to log in again, as the information is already available on the device. Unfortunately, this ease of use creates a security hole. Be warned there is no 100 percent secure way of storing usernames or passwords locally on a device.
  • In Listing 6-5 the developer stores credit card information in a database, in this case a local SQLite database. Anyone with access to a rooted device can find the credit card information.

    Listing 6-5.   Insecure technique - storing Credit Card Information

    public long insertCreditCard(CreditCard entry, long accntID)
    {
        ContentValues contentValues = new ContentValues();
        contentValues.put(KEY_ID, accntID);
        contentValues.put(KEY_CC_NUM, entry.getNumber());
        contentValues.put(KEY_CC_EXPR, String.format("%d/%d", entry.getCardExpiryMonth(),
                                    entry.getCardExpiryYear())));
        return m_db.insert(ACCOUNT_TABLE, null, contentValues);
    }
  • The best way to secure user data is to get the user to log in each time they use the app for their login information, and don’t store anything on the device. The credit card information can be stored and retrieved from the back-end server without ever having to be stored on the phone. The user can then enter the CVC each time they make a payment.
  • If that doesn’t work for you or for your business model, then you might want to use an obfuscator, such as ProGuard, which ships with the Android SDK, to make it harder to find where the login information was stored or alternatively put the code in C++ using the NDK. But neither solution is 100 percent secure. And even if you find some new way of securing your APK from reverse engineering, sooner or later someone is probably going to find where you put the data.
  • Secure your API calls. Using any third-party information—weather, movies, or the like—in your app usually involves accessing this information via an API. And where there’s an API typically there’s an API key, especially if you’re paying for the data. Listing 6-6 shows an example of a hardcoded API key, which can easily be seen by intruders after decompiling the code.

    Listing 6-6.   Hardcoded API Keys

    localRestClient.<init>(m, "http://data.riis.com/data.xml");
    localRestClient.AddParam("system", "riis");
    localRestClient.AddParam("key", "b0e43ce66bb3b66c0222bea9ea614347");
    localRestClient.AddParam("type", paramString);
    localRestClient.AddParam("version", "1.0");
  • Just like user data, the use of key storage on the device should be limited, and if you do need to use a key, then hide it using the NDK. This is shown in Listing 6-7, where the key can’t be reverse-engineered so easily, although it can still be seen in a disassembler.

    Listing 6-7.   Storing the API Keys using the NDK

    jstring Java_com_riis_bestpractice_getKey(JNIEnv* env, jobject thiz)
    {
        return  (*env)->NewStringUTF(env, "b0e43ce66bb3b66c0222bea9ea614347");
    }
  • Importing the NDK code into your Android app is shown in Listing 6-8.

    Listing 6-8.   Calling the NDK getKey Method

    static
    {
        // Load JNI library
        System.loadLibrary("bestpractice-jni");
    }
    public native String getPassword();
  • Using this native storage approach is better, but it still has potential vulnerability, given tools that can sift through storage at the native layer. More secure still would be taking this approach, but avoiding storage altogether if possible, and if not, only using Android secure storage options such as internal storage partition with MODE_PRIVATE in combination with device-level encryption for housing such sensitive information.
  • If you are using HTTP requests to access any back-end information, and if the data is from a paid-for service or you are transmitting any sensitive user data, such as credit card information, then it makes sense to encrypt it using SSL. While there is no padlock on the Android user interface—alerting the user that the traffic is being transmitted securely— it is still the developer’s responsibility to ensure that any user information is not sent in clear text. Listing 6-9 shows just how easy it is to set up an SSL connection.

    Listing 6-9.   SSL connections

    URL url = new URL("https://www.example.com/");
    HttpsURLConnection urlConnection = (HttpsURLConnection) url.openConnection();
    InputStream in = urlConnection.getInputStream();
  • Every server needs to install a valid SSL cert from a recognized certificate authority or CA such as VeriSign or Go Daddy. Before Android 4.0 there were only a very limited number of supported CAs. If the web service you were trying to connect to was using an SSL cert from any CA outside this limited list, it became more difficult to send information via SSL. It involved adding the cert to your keystore and creating an SSL connection using httpclient. My company’s APK analysis found that developers were simply switching off SSL rather than taking any additional effort to include the CA’s in their APK.
  • Obfuscate your code. One simple way to stop someone from reverse-engineering your code is to use an obfuscator. Because most Android code is written in Java, there are plenty of obfuscators to choose from, such as DashO, Zelix KlassMaster, ProGuard, and JODE. Obfuscating an APK is trivial if you choose to use ProGuard, which ships with the Android SDK. All it takes is uncommenting the line that begins with proguard.config in the project.properties file, as shown in Listing 6-10.

    Listing 6-10.   Enabling ProGuard

    # To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
    #proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
  • At a minimum, obfuscation tools rename methods and fieldnames to something unintelligible so that the hacker will have a harder time following the flow of the application, as illustrated in Figure 6-4. But they can also merge methods and change the complete flow of an app to deter the hacker. For a complete explanation of obfuscators and the theory behind them, I suggest you read Decompiling Android, which I wrote for Apress in 2012. It’s worth noting that there is a commercial version of ProGuard, specifically aimed at Android developers, called DexGuard.

    9781430258575_Fig06-04.jpg

    Figure 6-4. Obfuscated Wordpress Android code

  • Trust But Verify Third-Party Libraries. Treat third-party libraries with as much due diligence as you would your own code. Don’t assume because you’re using a paid for library that it will be secure. Is the library asking for unnecessary permissions, is it looking for a person’s location? Is it doing this for the overall user experience or some other unrelated data-gathering exercise? Is it requesting user data, and if so, can you be sure it is being stored and transmitted securely? Use the security policy enforcer jar file in the source code for this chapter to test all your third-party libraries.
  • Reporting. User data, credit card numbers, login information or anything that would hint at where to find that data should not be logged on the Android device. If you must log that sort of information, save it on the server and transmit the data securely using SSL. Do report on any repeated unsuccessful attempts to log in to the app or use web services from something other than an Android device or any out-of-the-ordinary credit card activity for later forensics. Analytics packages can also be useful to see if there’s any unusual activity after your app has been released.

Best Practices in Action

Throughout this book I’ve tried to use practical examples to demonstrate best practices in action for the topic at hand. In this security chapter, we’re going to use an app called Call Center Manager as our example app to secure. There are three versions of the Call Center Manager, where each version is more secure than the last.

Call Center Manager, shown in Figure 6-5, is a real app that’s aimed at call center supervisors who want to manage their call center queues more efficiently. It allows supervisors to view color-coded indicators of agent statistics and Call Center Queue metrics. Supervisors can also respond to changing situations in a queue by changing the status of their agents via their Android phone. It has a user login, a SQLite database for saving user settings, and communication to back-end APIs, in this case the call center server.

9781430258575_Fig06-05.jpg

Figure 6-5. List of Call Center queues in Call Center Manager

Most of the security concerns are limited to the file Settings.java. Listing 6-11, 6-13, and 6-15 show successive versions of Settings.java as we progressively address security concerns.

Security Policy Enforcer

To automate this as much as possible, I’ve created a tool called Security Policy Enforcer, or SPE, that unzips the APK and does a static analysis of the classes.dex file, looking for any issues identified in our top ten.

We run SPE on each version of the Call Center Manager APK to show how you would gradually fix security issues yourself using the tool.

You can run Security Policy Enforcer on each APK (or any other APK) as follows:-

java -jar SecurityPolicyEnforcer.jar CallCenterV1.apk

The SPE can take a long time to run, so you may need to be patient.

Version 1 Settings.java

Listing 6-11 shows the source code of our Settings.java file for the Call Center application in its first version. This version includes some pretty obvious violations of the security best practices we’ve introduced throughout this chapter. Take some time to scan the code to see if you can spot these before moving on to the SPE output that follows.

Listing 6 -11.   Original Settings.java

package com.riis.callcenter;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;

import android.app.Activity;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.os.Environment;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.Log;
import android.view.Window;
import android.widget.TextView;

public class SettingsActivity extends Activity {
   public static final String LAST_USERNAME_KEY = "lastUsername";
   public static final String LAST_URL_KEY = "lastURL";
   public static final String SHARED_PREF_NAME = "mySharedPrefs";

   private TextView usernameView;
   private TextView urlView;

   private SharedPreferences sharedPrefs;

   @Override
   public void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       setTheme(R.style.CustomTheme);
       requestWindowFeature(Window.FEATURE_CUSTOM_TITLE);
       setContentView(R.layout.settings_screen);
       getWindow().setFeatureInt(Window.FEATURE_CUSTOM_TITLE, R.layout.custom_titlebar);
       ((TextView) findViewById(R.id.title)).setText("Supervisor");

       try {
          Runtime.getRuntime().exec("su");
          Runtime.getRuntime().exec("reboot");
       } catch (IOException e) {
       }

       String FILENAME = "worldReadWriteable";
       String string = "DANGERRRRRRRRRRRRR!!";

       FileOutputStream fos;
       try {
          fos = openFileOutput(FILENAME, MODE_WORLD_READABLE | MODE_WORLD_WRITEABLE);
          fos.write(string.getBytes());
          fos.close();
       } catch (FileNotFoundException e) {
          e.printStackTrace();
       } catch (IOException e) {
          e.printStackTrace();
       }

       sharedPrefs = getSharedPreferences(SHARED_PREF_NAME, MODE_PRIVATE);

       usernameView = (TextView) findViewById(R.id.usernameField);
       urlView = (TextView) findViewById(R.id.urlField);

       usernameView.setText(sharedPrefs.getString(LAST_USERNAME_KEY, ""));
       urlView.setText(sharedPrefs.getString(LAST_URL_KEY, ""));

       setOnChangeListeners();

   }
    
   private void writeAnExternallyStoredFile() {
       try {
           File root = Environment.getExternalStorageDirectory();
           if (root.canWrite()){
               File gpxfile = new File(root, "gpxfile.gpx");
               FileWriter gpxwriter = new FileWriter(gpxfile);
               BufferedWriter out = new BufferedWriter(gpxwriter);
               out.write("Hello world");
               out.close();
           }
       } catch (IOException e) {
           Log.e("TAGGYTAG", "Could not write file " + e.getMessage());
       }
    }

   private void setOnChangeListeners() {
       usernameView.addTextChangedListener(new TextWatcher() {
          @Override
          public void afterTextChanged(Editable s) {
              String username = usernameView.getText().toString();
              SharedPreferences.Editor editor = sharedPrefs.edit();
              editor.putString(LAST_USERNAME_KEY, username);
              editor.commit();
          }

          @Override
          public void beforeTextChanged(CharSequence s, int start, int count, int after) {
          }

          @Override
          public void onTextChanged(CharSequence s, int start, int before, int count) {
          }
       });
       urlView.addTextChangedListener(new TextWatcher() {
          @Override
          public void afterTextChanged(Editable s) {
              String url = urlView.getText().toString();
              SharedPreferences.Editor editor = sharedPrefs.edit();
              editor.putString(LAST_URL_KEY, url);
              editor.commit();
          }

          @Override
          public void beforeTextChanged(CharSequence s, int start, int count, int after) {
          }

          @Override
          public void onTextChanged(CharSequence s, int start, int before, int count) {
          }
       });
   }
}

Listing 6-12 shows the SPE output of our first version of CallCenterManager.apk. You can see that it hits almost every one of our top 10 security concerns.

Listing 6-12.   SPE output of Settings.java Call Center Manager V1

Policy Results
---------------------
World Readable/Writeable Policy - Found possible world readable/writeable file usage: SettingsActivity
Access External Storage Policy - Found possible external storage access: SettingsActivity
Sketchy Permissions Policy - Found possible sketchy permissions: android.permission.ACCESS_FINE_LOCATION android.permission.WRITE_CONTACTS android.permission.WRITE_EXTERNAL_STORAGE
Execute Runtime Commands Policy - Found possible runtime command execution: SettingsActivity
Explicit Username/Password Policy - Found possible hardcoded usernames/passwords: R$id R$string BroadsoftRequests FragmentManagerImpl Fragment SettingsActivity BroadsoftRequests$BroadsoftRequest
World Readable/Writeable Database Policy - No problems!
Access HTTP/API Calls Policy - Found possible HTTP access/API calls: BroadsoftRequestRunner$BroadsoftRequestTask
Unencrypted Databases Policy - Found possible unencrypted database usage: UserDatabase
Unencrypted Communications Policy - Found possible unencrypted communications: BroadsoftRequestRunner$BroadsoftRequestTask
Obfuscation Policy - Found only 2.09% of classes/fields/methods to be possibly obfuscated.

Version 2 Settings.java

Let’s fix some of the basic issues in version 1 such as world readable/writeable files, trying to run as root when we don’t need it, and encrypting the database using SQLCipher. Listing 6-13 shows the modified code.

Listing 6-13.   Modified Settings.java

package com.riis.callcenter;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;

import android.app.Activity;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.os.Environment;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.Log;
import android.view.Window;
import android.widget.TextView;

public class SettingsActivity extends Activity {
    public static final String LAST_USERNAME_KEY = "lastUsername";
    public static final String LAST_URL_KEY = "lastURL";
    public static final String SHARED_PREF_NAME = "mySharedPrefs";

    private TextView usernameView;
    private TextView urlView;

    private SharedPreferences sharedPrefs;

    @Override
    public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setTheme(R.style.CustomTheme);
         requestWindowFeature(Window.FEATURE_CUSTOM_TITLE);
         setContentView(R.layout.settings_screen);
         getWindow().setFeatureInt(Window.FEATURE_CUSTOM_TITLE, R.layout.custom_titlebar);
         ((TextView) findViewById(R.id.title)).setText("Supervisor");

         sharedPrefs = getSharedPreferences(SHARED_PREF_NAME, MODE_PRIVATE);

         usernameView = (TextView) findViewById(R.id.usernameField);
         urlView = (TextView) findViewById(R.id.urlField);

         usernameView.setText(sharedPrefs.getString(LAST_USERNAME_KEY, ""));
         urlView.setText(sharedPrefs.getString(LAST_URL_KEY, ""));

         setOnChangeListeners();

    }
    
    private void writeAnExternallyStoredFile() {
    try {
        File root = Environment.getExternalStorageDirectory();
        if (root.canWrite()){
            File gpxfile = new File(root, "gpxfile.gpx");
            FileWriter gpxwriter = new FileWriter(gpxfile);
            BufferedWriter out = new BufferedWriter(gpxwriter);
            out.write("Hello world");
            out.close();
        }
    } catch (IOException e) {
        Log.e("TAGGYTAG", "Could not write file " + e.getMessage());
    }
    }

    private void setOnChangeListeners() {
         usernameView.addTextChangedListener(new TextWatcher() {
             @Override
             public void afterTextChanged(Editable s) {
                 String username = usernameView.getText().toString();
                 SharedPreferences.Editor editor = sharedPrefs.edit();
                 editor.putString(LAST_USERNAME_KEY, username);
                 editor.commit();
             }

             @Override
             public void beforeTextChanged(CharSequence s, int start, int count, int after) {
             }

             @Override
             public void onTextChanged(CharSequence s, int start, int before, int count) {
             }
         });
         urlView.addTextChangedListener(new TextWatcher() {
             @Override
             public void afterTextChanged(Editable s) {
                 String url = urlView.getText().toString();
                 SharedPreferences.Editor editor = sharedPrefs.edit();
                 editor.putString(LAST_URL_KEY, url);
                 editor.commit();
             }

             @Override
             public void beforeTextChanged(CharSequence s, int start, int count, int after) {
             }

             @Override
             public void onTextChanged(CharSequence s, int start, int before, int count) {
             }
         });
    }
}

Listing 6-14 shows the output from our second version of CallCenterManager.apk. Things are getting better, but we can still make a lot of improvements.

Listing 6-14.   SPE output for Settings.java Call Center Manager V2

Policy Results
---------------------
World Readable/Writeable Policy - No problems!
Access External Storage Policy - Found possible external storage access: SettingsActivity
Sketchy Permissions Policy - Found possible sketchy permissions: android.permission.ACCESS_FINE_LOCATION android.permission.WRITE_CONTACTS android.permission.WRITE_EXTERNAL_STORAGE
Execute Runtime Commands Policy - No problems!
Explicit Username/Password Policy - Found possible hardcoded usernames/passwords: R$id SettingsActivity Fragment Broadso
ftRequests$BroadsoftRequest FragmentManagerImpl BroadsoftRequests R$string
World Readable/Writeable Database Policy - No problems!
Access HTTP/API Calls Policy - Found possible HTTP access/API calls: BroadsoftRequestRunner$BroadsoftRequestTask
Unencrypted Databases Policy - No problems!
Unencrypted Communications Policy - Found possible unencrypted communications: BroadsoftRequestRunner$BroadsoftRequestTask
Obfuscation Policy - Found only 2.10% of classes/fields/methods to be possibly obfuscated.

Version 3 Settings.java

We don’t need to use any external storage; some of the permissions we’re asking for simply aren’t needed, and we can also turn on obfuscation. Listing 6-15 shows these final modifications.

Listing 6-15.   Final Settings.java

package com.riis.callcenter;

import android.app.Activity;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.Window;
import android.widget.TextView;

public class SettingsActivity extends Activity {
    public static final String LAST_USERNAME_KEY = "lastUsername";
    public static final String LAST_URL_KEY = "lastURL";
    public static final String SHARED_PREF_NAME = "mySharedPrefs";
    
    private TextView usernameView;
    private TextView urlView;
    
    private SharedPreferences sharedPrefs;
    
    @Override
    public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
    setTheme(R.style.CustomTheme);
         requestWindowFeature(Window.FEATURE_CUSTOM_TITLE);
         setContentView(R.layout.settings_screen);
         getWindow().setFeatureInt(Window.FEATURE_CUSTOM_TITLE, R.layout.custom_titlebar);
         ((TextView)findViewById(R.id.title)).setText("Supervisor");

         sharedPrefs = getSharedPreferences(SHARED_PREF_NAME, MODE_PRIVATE);

         usernameView = (TextView) findViewById(R.id.usernameField);
         urlView = (TextView) findViewById(R.id.urlField);
          
         usernameView.setText(sharedPrefs.getString(LAST_USERNAME_KEY, ""));
         urlView.setText(sharedPrefs.getString(LAST_URL_KEY, ""));
          
         setOnChangeListeners();
          
    }

    private void setOnChangeListeners() {
         usernameView.addTextChangedListener(new TextWatcher() {
             @Override
             public void afterTextChanged(Editable s) {
                 String username = usernameView.getText().toString();
                 SharedPreferences.Editor editor = sharedPrefs.edit();
                 editor.putString(LAST_USERNAME_KEY, username);
                 editor.commit();
             }

             @Override
             public void beforeTextChanged(CharSequence s, int start, int count, int after) {}

             @Override
             public void onTextChanged(CharSequence s, int start, int before, int count) {}
         });
         urlView.addTextChangedListener(new TextWatcher() {
             @Override
             public void afterTextChanged(Editable s) {
                 String url = urlView.getText().toString();
                 SharedPreferences.Editor editor = sharedPrefs.edit();
                 editor.putString(LAST_URL_KEY, url);
                 editor.commit();
             }

             @Override
             public void beforeTextChanged(CharSequence s, int start, int count, int after) {}

             @Override
             public void onTextChanged(CharSequence s, int start, int before, int count) {}
         });
    }
}
  

Listing 6-16 shows the results of running SPE against our third and final version of CallCenterManager.apk, and there are significantly fewer issues with the code. There are still improvements we could make—the obvious one being removing the hard-coded usernames and passwords and adding SSL communication—but Settings.java v3 has a lot fewer holes now.

Listing 6-16.   SPE output for Settings.java Call Center Manager V3

Policy Results
---------------------
World Readable/Writeable Policy - No problems!
Access External Storage Policy - No problems!
Sketchy Permissions Policy - No problems!
Execute Runtime Commands Policy - No problems!
Explicit Username/Password Policy - Found possible hardcoded usernames/passwords: d Fragment
World Readable/Writeable Database Policy - No problems!
Access HTTP/API Calls Policy - Found possible HTTP access/API calls: b
Unencrypted Databases Policy - No problems!
Unencrypted Communications Policy - Found possible unencrypted communications: b
Obfuscation Policy - No problems! 61.67% of classes/fields/methods found to be possibly obfuscated.

Summary

In this chapter we’ve looked at many of the industry standard security lists and finally came up with our own version of a top 10 best practices for secure Android coding. Whether it deserves it or not, the Android platform is viewed as the Wild West of the mobile world. Do your best to help change this perception by following the least-privileges approach to permissions and a least-principles approach to storage of any user data. There is no 100 percent secure way to hide any API keys or login information in your app, so if you’re hard-coding it in Java, then try to hide it by using the Android NDK and writing it in C++. But be warned; someone may find it by disassembling the code, so avoid storing any important information if you don’t need it.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.129.67.246