6

we use some Networkcredentials in out App. I just decompiled the app and was able to see the Credentials like Name and Password. I do not really get how to prevent this. I think the word "obfuscator" is the direction which I have to go. We test proguard but it does not have string encryption or am I wrong?

Is there an easy and free way to do this?

Thank you.

Benjamin Wolf
  • 93
  • 1
  • 9
  • 1
    If you store the password in the application, it makes only a small difference if it is stored in plain or encrypted. As you did, everyone else can do to decompile the application. – SubOptimal Jan 19 '15 at 11:51
  • But there have to be a way to realize this. Or am I wrong? For example what if you would want to use JDBC to connect to a Mysql-database. There you have to set your credentials. – Benjamin Wolf Jan 19 '15 at 11:57
  • Absolutely wrong. You have to *encipher* or *hash* your password. – Giulio Biagini Jan 19 '15 at 11:58
  • What is username and password used for? Ideally, you should avoid storing them in the app itself. – Wayne Ellery Jan 19 '15 at 12:14
  • For example if I woult want to use JDBC. Or if I would want to download a file from a defined FTP-Server. – Benjamin Wolf Jan 19 '15 at 12:26
  • 1
    Normally in these situations you would take the username and password from some form of configuration file that is only accessible on the specific instance where the server is running. If you can figure out the value of the password from your code then so can everyone else. – Sebastiaan van den Broek Jan 19 '15 at 12:29
  • 1
    The solution depends on the level of security you would be satisfied with. Simple obfuscation is possible by running a string literal (or a byte array) through some (un)mangling algorithm to retrieve the password. – Marko Topolnik Jan 19 '15 at 12:30

3 Answers3

8

Sorry, but this simply does't work no matter what you'll try. If you obfuscate / encrypt the credentials, the program still must be able to decrypt them at run-time. Therefore, the encryption keys must also be in the generated bytecode somewhere and therefore it's possible to take them, and decrypt the credentials manually outside the program (or just step through the program and read the credentials once they're decrypted).

What you're trying to do is Security by Obscurity and it doesn't work.

Whatever you do, if the program can obtain the credentials at run-time without any external help, a skilled attacker can do the same given enough time.

What you should do:

  1. Store the credentials in plain-text in a property file. Don't bother with encryption, it's pointless. You must make sure the db user you're using is read-only or add-only or something similar so you prevent any damage.
  2. Let the user input the password. If it's not stored in the bytecode, it's safe. He could e.g. input his password and have an account in the db...
  3. Use a safe and known authentication mechanism. Plaintext login+password is not that.
  4. Don't let your application go anywhere near a DB. Set up a service somewhere, with an API, which would hold the read DB conenction. Your application could connect to that and get data via this API. This way, an attacker can't directly access your DB. He could call anything in the new service, though, so you must make sure there's no sensitive data accessible in there.
Petr Janeček
  • 37,768
  • 12
  • 121
  • 145
  • The real question here is what is your requested level of security, who are you protecting against, how valueable is your confidential data etc. – Petr Janeček Jan 19 '15 at 12:33
  • 4. i use JDBC. But i have to tell the JDBC API which connection it has to use. – Benjamin Wolf Jan 19 '15 at 12:35
  • 1
    @BenjaminWolf Not what I meant. You'd set up a new application, on a secured server, which would have the DB connection in plaintext. The server would be secure, no problems there. Your application would not connect to the DB, but it would connect to the secured server and run methods on the new application running there. The new application would be an interface for you to your database. If you can't design an API so that it would be safe from the db-standpoint, this silution won't work, though :(. – Petr Janeček Jan 19 '15 at 12:38
  • so this is like using PHP-scripts right? EDIT: i want to connect to a Mysql-Database and am using JDBC now. But there i have to give the credentials. In PHP i would not need to give the app the credentials but JDBC is so easy :/ – Benjamin Wolf Jan 19 '15 at 12:40
  • @BenjaminWolf No need for PHP, but yes, you're basically right. Set up an application server on a safe machine. This server will connect to the DB (you can do this in Java, so using JDBC, no problem) and then connect your app to this server (search "java messaging" or "RPC"). Let your server query the data from DB and send it to your application. – Petr Janeček Jan 19 '15 at 14:12
  • Security by obscurity, on overused mantra, is in reference to encryption algorithms. Obscurity to prevent reverse engineering and data hiding is completely reasonable. The goal is not perfect security. – Cameron Lowell Palmer May 11 '21 at 07:24
3

You should consider to encipher the username and the password: How to encrypt String in Java.

// bytes to encrypt
byte[] input;

// the key and the initialization vector
byte[] keyBytes;
byte[] ivBytes;

// initialize the Cipher
SecretKeySpec key = new SecretKeySpec(keyBytes, "DES");
IvParameterSpec ivSpec = new IvParameterSpec(ivBytes);
Cipher cipher = Cipher.getInstance("DES/CBC/PKCS5Padding");

// encryption
cipher.init(Cipher.ENCRYPT_MODE, key, ivSpec);
byte[] encrypted= new byte[cipher.getOutputSize(input.length)];
int enc_len = cipher.update(input, 0, input.length, encrypted, 0);
enc_len += cipher.doFinal(encrypted, enc_len);

// decryption
cipher.init(Cipher.DECRYPT_MODE, key, ivSpec);
byte[] decrypted = new byte[cipher.getOutputSize(enc_len)];
int dec_len = cipher.update(encrypted, 0, enc_len, decrypted, 0);
dec_len += cipher.doFinal(decrypted, dec_len);

Usually, the key (bytes array) should be stored in a file that is only accessible on the specific instance where the server is running and not coded into the app source file.

Otherwise you can use hash (e.g: md5 or sha1) and store the fingerprint instead of the plain string:

// SHA1("hello world") -> "2aae6c35c94fcfb415dbe95f408b9ce91ee846ed

This is a simple method which allows you to calculate the SHA1 hash of a string:

public static String SHA1(String text) throws NoSuchAlgorithmException, UnsupportedEncodingException { 
    MessageDigest md = MessageDigest.getInstance("SHA-1");
    md.update(text.getBytes("iso-8859-1"));
    byte[] hash = md.digest();

    Formatter formatter = new Formatter();
    for (byte b : hash)
        formatter.format("%02x", b);
    return formatter.toString();
}

Import java.io.UnsupportedEncodingException, java.security.MessageDigest and java.security.NoSuchAlgorithmException are required.

Community
  • 1
  • 1
Giulio Biagini
  • 935
  • 5
  • 8
  • 2
    This is the right way to verify a password that's entered - but as I understand the question the OP's app has to authenticate to some other server - and thus needs to send a password. And hashing won't help you there. – piet.t Jan 19 '15 at 12:07
  • yes, right, I need to send the password to a server :/ – Benjamin Wolf Jan 19 '15 at 12:11
  • Ok, now, hashing still remain a valid method, used together with the *encryption* method: http://stackoverflow.com/questions/1205135/how-to-encrypt-string-in-java – Giulio Biagini Jan 19 '15 at 12:24
  • 1
    Hashing is not a valid method because hashing cannot be reversed (in theory), also encryption is pointless because you still need to be able to reverse the encryption from code. – Sebastiaan van den Broek Jan 19 '15 at 12:28
  • Just edited to make more complete the answer, better specifying how to encrypt a string. – Giulio Biagini Jan 19 '15 at 12:45
  • That's nice but not really what I need :/ This would be helpfull if I would handle passwords in the app but I only want to write the password in the app-code to connect to a Mysql-DB – Benjamin Wolf Jan 19 '15 at 12:50
0

Your issue is related to encryption and not obfuscation. You may use this library to store the credentials in an encrypted way: http://www.jasypt.org/encrypting-configuration.html There are different ways to pass the encryption key to it.

Otherwise, depending on your context, consider using different authentication mechanisms (SSO like) instead of login/password.

Guy Bouallet
  • 2,099
  • 11
  • 16