Handling passwords used for auth in source code

Assuming I'm trying to extract from a RESTful api that uses basic certificates of authenticity / basic certificates, what would be the best way to store this username and password in my program? Right now he's just sitting in clear text.

UsernamePasswordCredentials creds = new UsernamePasswordCredentials("myName@myserver","myPassword1234"); 

Is there a way to do this with more caution?

thank

+58
java security authentication
Oct 17 '12 at 15:17
source share
5 answers

With inner thinking, here are a few steps to protect your process:




The first step is to change your password processing from String to character array .

The reason for this is that String is an immutable object, and therefore its data will not be cleared immediately, even if this object is set to null ; Instead, the data is set up to collect garbage, and this creates a security problem because malware can access this String data (password) before it is cleared.

This is the main reason why the getText() method of Swing JPasswordField is deprecated, and why getPassword() uses character arrays .




The second step is to encrypt your credentials, only temporarily decrypting them during the authentication process.

This, like the first step, ensures that the time of your vulnerability is as short as possible.

It is recommended that your credentials not be hard-coded and instead store them in a centralized, customizable, and easy-to-maintain way, such as a configuration or properties file.

You must encrypt your credentials before saving the file, and in addition, you can apply a second encryption to the file itself (two-level encryption for the credentials and 1-layer encryption to the other contents of the file).

Please note that each of the two encryption processes mentioned above can be multi-level. Each encryption can be a separate application of the standard triple data encryption (AKA TDES and 3DES) as a conceptual example.




After your local environment is properly secured (but remember that it will never be “secure”!), The third step is to apply basic protection to your transfer process using TLS (Transport Layer Security) or SSL (Secure Sockets Layer) .




The fourth step is to apply other protection methods.

For example, applying obfuscation techniques to your “used” compiler to avoid (even soon) the impact of your security measures if your program is received by Ms. Eva, Mr. Mallory or someone else (bad guys) and decompiled .




UPDATE 1:

For @ Damien.Bell, here is an example that covers the first and second steps:

  //These will be used as the source of the configuration file stored attributes. private static final Map<String, String> COMMON_ATTRIBUTES = new HashMap<String, String>(); private static final Map<String, char[]> SECURE_ATTRIBUTES = new HashMap<String, char[]>(); //Ciphering (encryption and decryption) password/key. private static final char[] PASSWORD = "Unauthorized_Personel_Is_Unauthorized".toCharArray(); //Cipher salt. private static final byte[] SALT = { (byte) 0xde, (byte) 0x33, (byte) 0x10, (byte) 0x12, (byte) 0xde, (byte) 0x33, (byte) 0x10, (byte) 0x12,}; //Desktop dir: private static final File DESKTOP = new File(System.getProperty("user.home") + "/Desktop"); //File names: private static final String NO_ENCRYPTION = "no_layers.txt"; private static final String SINGLE_LAYER = "single_layer.txt"; private static final String DOUBLE_LAYER = "double_layer.txt"; /** * @param args the command line arguments */ public static void main(String[] args) throws GeneralSecurityException, FileNotFoundException, IOException { //Set common attributes. COMMON_ATTRIBUTES.put("Gender", "Male"); COMMON_ATTRIBUTES.put("Age", "21"); COMMON_ATTRIBUTES.put("Name", "Hypot Hetical"); COMMON_ATTRIBUTES.put("Nickname", "HH"); /* * Set secure attributes. * NOTE: Ignore the use of Strings here, it being used for convenience only. * In real implementations, JPasswordField.getPassword() would send the arrays directly. */ SECURE_ATTRIBUTES.put("Username", "Hypothetical".toCharArray()); SECURE_ATTRIBUTES.put("Password", "LetMePass_Word".toCharArray()); /* * For demosntration purposes, I make the three encryption layer-levels I mention. * To leave no doubt the code works, I use real file IO. */ //File without encryption. create_EncryptedFile(NO_ENCRYPTION, COMMON_ATTRIBUTES, SECURE_ATTRIBUTES, 0); //File with encryption to secure attributes only. create_EncryptedFile(SINGLE_LAYER, COMMON_ATTRIBUTES, SECURE_ATTRIBUTES, 1); //File completely encrypted, including re-encryption of secure attributes. create_EncryptedFile(DOUBLE_LAYER, COMMON_ATTRIBUTES, SECURE_ATTRIBUTES, 2); /* * Show contents of all three encryption levels, from file. */ System.out.println("NO ENCRYPTION: \n" + readFile_NoDecryption(NO_ENCRYPTION) + "\n\n\n"); System.out.println("SINGLE LAYER ENCRYPTION: \n" + readFile_NoDecryption(SINGLE_LAYER) + "\n\n\n"); System.out.println("DOUBLE LAYER ENCRYPTION: \n" + readFile_NoDecryption(DOUBLE_LAYER) + "\n\n\n"); /* * Decryption is demonstrated with the Double-Layer encryption file. */ //Descrypt first layer. (file content) (REMEMBER: Layers are in reverse order from writing). String decryptedContent = readFile_ApplyDecryption(DOUBLE_LAYER); System.out.println("READ: [first layer decrypted]\n" + decryptedContent + "\n\n\n"); //Decrypt second layer (secure data). for (String line : decryptedContent.split("\n")) { String[] pair = line.split(": ", 2); if (pair[0].equalsIgnoreCase("Username") || pair[0].equalsIgnoreCase("Password")) { System.out.println("Decrypted: " + pair[0] + ": " + decrypt(pair[1])); } } } private static String encrypt(byte[] property) throws GeneralSecurityException { SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBEWithMD5AndDES"); SecretKey key = keyFactory.generateSecret(new PBEKeySpec(PASSWORD)); Cipher pbeCipher = Cipher.getInstance("PBEWithMD5AndDES"); pbeCipher.init(Cipher.ENCRYPT_MODE, key, new PBEParameterSpec(SALT, 20)); //Encrypt and save to temporary storage. String encrypted = Base64.encodeBytes(pbeCipher.doFinal(property)); //Cleanup data-sources - Leave no traces behind. for (int i = 0; i < property.length; i++) { property[i] = 0; } property = null; System.gc(); //Return encryption result. return encrypted; } private static String encrypt(char[] property) throws GeneralSecurityException { //Prepare and encrypt. byte[] bytes = new byte[property.length]; for (int i = 0; i < property.length; i++) { bytes[i] = (byte) property[i]; } String encrypted = encrypt(bytes); /* * Cleanup property here. (child data-source 'bytes' is cleaned inside 'encrypt(byte[])'). * It not being done because the sources are being used multiple times for the different layer samples. */ // for (int i = 0; i < property.length; i++) { //cleanup allocated data. // property[i] = 0; // } // property = null; //de-allocate data (set for GC). // System.gc(); //Attempt triggering garbage-collection. return encrypted; } private static String encrypt(String property) throws GeneralSecurityException { String encrypted = encrypt(property.getBytes()); /* * Strings can't really have their allocated data cleaned before CG, * that why secure data should be handled with char[] or byte[]. * Still, don't forget to set for GC, even for data of sesser importancy; * You are making everything safer still, and freeing up memory as bonus. */ property = null; return encrypted; } private static String decrypt(String property) throws GeneralSecurityException, IOException { SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBEWithMD5AndDES"); SecretKey key = keyFactory.generateSecret(new PBEKeySpec(PASSWORD)); Cipher pbeCipher = Cipher.getInstance("PBEWithMD5AndDES"); pbeCipher.init(Cipher.DECRYPT_MODE, key, new PBEParameterSpec(SALT, 20)); return new String(pbeCipher.doFinal(Base64.decode(property))); } private static void create_EncryptedFile( String fileName, Map<String, String> commonAttributes, Map<String, char[]> secureAttributes, int layers) throws GeneralSecurityException, FileNotFoundException, IOException { StringBuilder sb = new StringBuilder(); for (String k : commonAttributes.keySet()) { sb.append(k).append(": ").append(commonAttributes.get(k)).append(System.lineSeparator()); } //First encryption layer. Encrypts secure attribute values only. for (String k : secureAttributes.keySet()) { String encryptedValue; if (layers >= 1) { encryptedValue = encrypt(secureAttributes.get(k)); } else { encryptedValue = new String(secureAttributes.get(k)); } sb.append(k).append(": ").append(encryptedValue).append(System.lineSeparator()); } //Prepare file and file-writing process. File f = new File(DESKTOP, fileName); if (!f.getParentFile().exists()) { f.getParentFile().mkdirs(); } else if (f.exists()) { f.delete(); } BufferedWriter bw = new BufferedWriter(new FileWriter(f)); //Second encryption layer. Encrypts whole file content including previously encrypted stuff. if (layers >= 2) { bw.append(encrypt(sb.toString().trim())); } else { bw.append(sb.toString().trim()); } bw.flush(); bw.close(); } private static String readFile_NoDecryption(String fileName) throws FileNotFoundException, IOException, GeneralSecurityException { File f = new File(DESKTOP, fileName); BufferedReader br = new BufferedReader(new FileReader(f)); StringBuilder sb = new StringBuilder(); while (br.ready()) { sb.append(br.readLine()).append(System.lineSeparator()); } return sb.toString(); } private static String readFile_ApplyDecryption(String fileName) throws FileNotFoundException, IOException, GeneralSecurityException { File f = new File(DESKTOP, fileName); BufferedReader br = new BufferedReader(new FileReader(f)); StringBuilder sb = new StringBuilder(); while (br.ready()) { sb.append(br.readLine()).append(System.lineSeparator()); } return decrypt(sb.toString()); } 

A complete example regarding each step of the defense would far exceed what I consider to be reasonable for this question, since it is about "what are the steps" and not "how to apply them."

This would greatly overestimate my answer (finally, the sample), while the other questions here about SO are already focused on the “How” on these steps, being much more appropriate and offering a much better explanation and sample on the implementation of each individual step.

+91
Oct 19 '12 at 19:31
source share

If you are using basic auth, you should associate this with SSL to avoid sending your credentials in base64 encoded text format. You do not want anyone to sniff your packages to get your credentials. Also, do not write your credentials in source code. Make them customizable. read them from the configuration file. You must encrypt the credentials before saving them in the configuration file, and your application must decrypt the credentials after reading them from the configuration file.

+7
Oct 17
source share
  • A secure computer that initiates the request (your computer). if this machine is unsafe, nothing will protect you. which is a completely separate topic (modern software, proper configuration, strong passwords, encrypted swap, hardware sniffers, physical security, etc.).
  • Protect your vault The environment you use to store your credentials must be encrypted. decrypted credentials should only be stored in the drum of your secure machine.
  • people who support this equipment should be trusted (possibly the weakest link)
  • they should also know as little as possible. that protection against rubber hose cryptanalysis
  • Your credentials must comply with all security recommendations (correct length, randomness, single purpose, etc.).
  • Your connection to the remote service must be secured (SSL, etc.)
  • you must trust your remote service (see paragraphs 1-4). plus it must be susceptible to hacking (if your data / service is unsafe, then protecting your credentials is pointless). plus it does not have to store your credentials.

plus maybe thousands of things i forgot about :)

+2
Oct 20
source share

As a general rule, it is not recommended that you encrypt credentials. Something that is encrypted can be decrypted. A common practice is to save passwords as a salt hash . The hash cannot be decrypted. Salt is added to defeat brute force with the help of Rainbow Tables . As long as each user has their own random salt, the attacker will have to create a set of tables for each possible salt value, which quickly makes this attack impossible during the life of the Universe. This is why websites usually cannot send you your password if you forget it, but they can only "reset". They do not have a saved password, only its hash.

Password hashing is not very difficult to implement on your own, but it is such a common problem to solve that many others did it for you. I found jBcrypt easy to use.

As an additional protection against brute force password guessing, the most common practice is forcing the user or the remote IP to wait a few seconds after a certain number of login attempts with the wrong password. Without this, an attacker can guess as many passwords per second as your server can handle. There is a huge difference between being able to guess 100 passwords in 10 seconds or a million.

I get the impression that you included a combination of username and password in the source code. This means that if you ever want to change the password, you will have to recompile, stop and restart the service, and this also means that anyone who receives your source code also has your passwords. Common best practice should never do this, but to store credentials (username, password hash, password salt) in your data warehouse

+1
Oct 26 '12 at 8:21
source share

If you cannot trust the environment in which your program runs, but you need to authenticate with simple passwords or certificates, you cannot do anything to protect your credentials. The most you can do is obfuscate them using the methods described in other answers.

As a workaround, I will run all requests in a RESTful api through a proxy server that you can trust, and do cleartext password authentication.

-one
Oct 26
source share



All Articles