Best way to secure Android app sensitive Data?

Yes this is a pretty general question but I'm trying to get a feel for the best way to handle an app that touches base w/ a webserver that distributes sensitive data to the app. Any links, general information advice etc.. would be appreciated.

Since the app would store persistant data retreived from the database for a certain amount of time.. everything becomes somewhat touchy.

57010 次浏览

If you want to pretty much ensure the user cannot see the data other than by looking at your app then encryption is really the only way. Even the "protected" storage is accessible to the user if a device is rooted. Even encryption is not totally secure as you need to decrypt the data at some point in order to display it. You will dissuade the casual browser but not the determined hacker.

Use SSL on HTTPS to transfer data instead of HTTP you need to setup the certificates on the webserver not very sure how it works..

If you are really concerned about the data then further encrypt it with a unique algorithm before sending and decrypt it when it reaches the app. I guess that's all about it.. Unless you need something really strong then develop your own protocol based on TCP and/or use another port.. maybe that'll help

http://en.wikipedia.org/wiki/Secure_Sockets_Layer http://developer.android.com/reference/javax/net/ssl/package-summary.html http://blog.synyx.de/2010/06/android-and-self-signed-ssl-certificates/

As for storing data in the app what you can encrypt the data before storing or you can use another format other than SQLite for better security as you can view sqlite databases using the browser pretty easily.

Unless the phone is rooted there should not be a way to extract the data from it.

Storing sensitive data on the device

That depends very much on your audience. Normally, the Android OS prohibits apps from accessing each other's files (i.e. databases, preference files, regular files stored in the app's private directory) through proven Linux file permissions. However, on rooted devices an application can obtain root access and read everything. A few things to think about:

  1. If you know your users won't have root (e.g. if you are not distributing the app through Android Market, but only in your company, or something like that), you can simply rely on Android's filesystem-based security.
  2. If a user does get root access, he will be very careful what application he gives that priviledge to
  3. If an app does get root access, it can wreak a lot of havoc. The information in your app could be the least of the user's worries.
  4. Rooting leads to zero warranty. Including in apps. You can't be held responsible for leaking information on a rooted phone.

To conclude, if your information is not super-duper sensitive (e.g. credit card information), I'd suggest just sticking with the default security provided by Android (i.e. save everything in plain text, knowing other apps can't access it).

Otherwise, encryption is the way to go. It's not 100% secure (a hacker could de-compile your app and figure out how to decrypt the data), but it's a major pain to crack and will stop most hackers. Especially if you obfuscate your code with something like ProGuard.


Transferring sensitive data from the server to the device

You have a few options here. First of all, always use HTTPS. After enabling HTTPS, here are two extra security measures I would propose:

  1. Use an API key system. Include this API key in all your requests and check it on the server side before sending any response back. Remember that since you're using HTTPS, an attacker would not be able to just use a network sniffer to find out your API key. However, this is pretty easy to figure out if someone decompiles your app, which is why you can obfuscate it even further (besides using ProGuard). For example, you can keep the API key broken up into pieces all around your code (for example as static members in two or three classes). Then, when you send a request, you just concatenate all those pieces. You can even apply some other sort of transformation (e.g. bit shifting) to make it even harder to figure out from the decompiled code.
  2. You can generate a key every time you send a request. That key would be generated by using a bit of logic that only you know, so that you can implement it client- and server-side as well. For example, a request could include the following parameters:
    time=1321802432&key=[generated-key]
    where generated-key is generated from the time parameter. For example: md5(time + salt). When the server receives this request, it can do two things:
    1. Check that key is indeed equal to md5(time + salt) (note that only the client and the server know the salt and it can be obfuscated similarly to the API key above), and
    2. Check that time is not too far back in the past (e.g. if it's more than 1-2 minutes in the past, consider the request invalid).

The second method is more useful if you are also doing plain HTTP requests, where everyone can see the parameters being sent. Also, it's much harder to figure out from decompiled code. Especially if you spread the key calculation logic across multiple classes.

However, note that nothing makes it impossible to crack your app. You can obfuscate as much as you want, if a hacker is really determined to get to your data, he will be able to so by decompiling your application and spending many sleepless nights passing through your code and figuring out how the requests are formed. The only real way of securing your data is by asking your user for a password, besides doing all the work I wrote about above. You can't get a password that only exists in someone's (the user) head from decompiled code :).

(Came here thanks to a Google search)

I've been researching this a lot lately and this page has come up a lot thanks to Google and Bing searches. The widely-accepted procedure for storing data on the device securely has been to use a strong encryption algorithm like AES. The harder question is "AES requires a secure key. What do you do with the key?"

Google recently announced a cloud-based storage solution for apps, so you could consider storing the key there if the situation allows. Otherwise, its seems that getting the key outside the device, like on a server, is better. If you can make the user punch in a PIN, that would actually work the best. You can do password derivation in order to store the password, and you can redo the derivation to verify the password

Without the "user punching in a PIN" part, I haven't found a lot of good answers to that question. However, DO NOT HARD-CODE THE KEY IF YOU MUST STORE ONE WITH THE APP. At the minimum, generate a key using a secure password generator and/or a derivation function like PBKDF2 (Password-based derivation function 2).

If I read the posts correctly, Google did say that one approach is to generate a key once the app starts the first time, store the key via the MODE_PRIVATE flag to a lot of file I/O operations, and use that as the key. You can also derive other keys based on that master key, and the NIST actually suggests something along that lines.

Whether or not to trust the master-key method, I'll leave to you. This key would get exposed on a rooted device. I'll also admit that I'm still researching the issue

Every application on Android runs in a secure sandbox environment, so other processes on the system cannot access your code or private data without proper handshake. But still there are many vulnerabilities are possible by poor design of the apps. This link from Android developers site advice you some of the good tips for security - https://developer.android.com/training/articles/security-tips.html

public String androidId = "";


public String encrypt(String value) {
try {
IvParameterSpec iv = new IvParameterSpec(INIT_VECTOR.getBytes("UTF-8"));
SecretKeySpec skeySpec = new SecretKeySpec(androidId.getBytes("UTF-8"), "AES");


Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);


byte[] encrypted = cipher.doFinal(value.getBytes());
return Base64.encodeToString(encrypted, Base64.DEFAULT);
} catch (Exception ex) {
ex.printStackTrace();
}
return null;
}


public String decrypt(String value) {
try {
IvParameterSpec iv = new IvParameterSpec(INIT_VECTOR.getBytes("UTF-8"));
SecretKeySpec skeySpec = new SecretKeySpec(androidId.getBytes("UTF-8"), "AES");


Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);
byte[] original = cipher.doFinal(Base64.decode(value, Base64.DEFAULT));


return new String(original);
} catch (Exception ex) {
ex.printStackTrace();
}


return null;
}


public void saveSecureData(String sdt) {
SharedPreferences settings = getApplicationContext().getSharedPreferences("TableName", 0);
SharedPreferences.Editor editor = settings.edit();
String encryptedString = encrypt(sdt);
editor.putString("FieldName", encryptedString.toString());
editor.apply();
}
public String readSecureData() {
SharedPreferences settings = getApplicationContext().getSharedPreferences("TableName", 0);
String homeScore = settings.getString("FieldName", "DEFAULT");
return homeScore;
}


public void testUnit() {


androidId = Settings.Secure.getString(getContentResolver(), Settings.Secure.ANDROID_ID);
String lastRecord = readSecureData();


if (lastRecord.equals("DEFAULT")) {


saveSecureData("Data to save...");




} else {




String decryptedString = decrypt(lastRecord);
Toast.makeText(MainActivity.this, lastRecord + " > " + decryptedString, Toast.LENGTH_LONG).show();




}


}
  • testUnit() : Sample function to test the code.
  • androidId : Device unique id that will set on testUnit function

androidId is a random unique code that was generated on first boot of device just one time in device life...

  • encrypt() : Convert its input string to encrypted data with androidId secret-key and return it as new string.
  • saveSecureData() : encrypt its input string and save it in android shared database.
  • readSecureData() : Return encrypted data as string from android shared database.
  • decrypt() : Convert its encrypted input string to its original format based on androidId secret-key and return new string.