Handling Passwords Used for Auth in Source Code

Handling passwords used for auth in source code



Important note:

If you're designing the authentication system as a whole, you shouldn't store passwords, even if they're encrypted. You store a hash, and check if passwords provided during login match the same hash. That way, a security breach on your database avoids getting your users' passwords exposed.

With that said, for situations where you are going to store data as-is (in this case passwords), then with an inner-to-outer mindset, here are some steps to protect your process:


First step, you should change your password-handling from String to character array.

The reason for this is that a String is an immutable object, and so it's data will not be cleansed immediately even if the object is set to null; The data is set for garbage-collection instead, and this poses security problems because malicious programs might gain access to that String (password) data before it is cleaned.

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


The second step is to encrypt your credentials, only decrypting them temporarily during the authentication process. Or to hash them server-side, store that hash, and "forget" the original password.

This, similarly to the first step, makes sure your vulnerability-time is as small as possible.

It is recommended that your credentials are not hard-coded, and that instead, you store them in a centralized, configurable and easily-maintainable manner, such as a configuration or properties file, or a database.

You should encrypt your credentials before saving the file, and additionally, you can apply a second encryption to the file itself (2-layer encryption to the credentials, and 1-layer to other file contents).

Note that each of the two encryption processes mentioned above can be multiple-layered themselves. Each encryption can be an individual application of Triple Data Encryption Standard (AKA TDES and 3DES), as a conceptual example.


After your local environment is properly protected (but remember, it's never ever "safe"!), the third step is apply basic protection to your transmission process, by using TLS (Transport Layer Security) or SSL (Secure Sockets Layer).


The forth step is to apply other protection methods.

For example, applying obfuscation techniques to your "to-use" compile, to avoid (even if shortly) the exposure of your security measures in case your program is obtained by Ms. Eve, Mr. Mallory, or someone else (the bad-guys) and decompiled.


UPDATE 1:

By @Damien.Bell 's request, here is an example that covers the first and second steps:

    //These will be used as the source of the configuration file's 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's 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's 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's 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 full example, addressing every protection step, would far exceed what I think is reasonable for this question, since it's about "what are the steps", not "how to apply them".

It would far over-size my answer (at last the sampling), while other questions here on S.O. are already directed on the "How to" of those steps, being far more appropriate, and offering far better explanation and sampling on the implementation of each individual step.

I need to securely store a username and password in Python, what are my options?

I recommend a strategy similar to ssh-agent. If you can't use ssh-agent directly you could implement something like it, so that your password is only kept in RAM. The cron job could have configured credentials to get the actual password from the agent each time it runs, use it once, and de-reference it immediately using the del statement.

The administrator still has to enter the password to start ssh-agent, at boot-time or whatever, but this is a reasonable compromise that avoids having a plain-text password stored anywhere on disk.

Protecting Sections of Source Code

It is not possible to do this. Even if you encrypt the login/password and store it somewhere (may it be your class or an external file) you'd still need to save the encryption key somewhere in plain text. This is actually just marginally better than saving username/password in plain text, in fact I would avoid doing so as it creates a false sense of security.

So I'd suggest that your class takes username/password as a parameter and that the system which is using your class will have to care about protecting the credentials. It could do so by asking an end user to enter the credentials or store them into an external file which is only readable to the operating system user that your process is running as.

Edit: You might also think about using mechanisms such as OAuth which use tokens instead of passwords. Tokens have a limited life time and are tied to a certain purpose so they pose a good alternative to access credentials. So your end users could get an access token with their,say, Windows credentials, which is then used inside your class to call the protected service.

Password storage in code. How to make it safe?

The safest way to protect your password is simply to not store it all, at least locally.

As soon as you store anything client-side the safety of the data is only as strong as the safety of the hardware it's sitting on. For example, if someone were to install your app on a public machine (e.g. internet cafe) anyone who knows what they are doing has the potential to get access to your password. It's less of a problem if you can be sure that the app is only going to be installed on private machines and only used by "good" users (which, ultimately, you can't).

How secure you need this password to be is really down to you. The questions you really need to ask yourself are

  • What sort of damage could be done if they did manage to get the password?
  • What measures do I have in place to detect any sort of misuse? (e.g. IP logging etc.)
  • What procedures do I have in place if someone was misusing the API? (e.g. password change)

The 3rd point poses a few problems when storing the password locally. If you detected misuse of the API and consequently changed the password, how can you cascade those changes down to the clients?

For me, the safest way to avoid all these issues is to have your app query your API server and have it return some sort of authentication token (aka API token). This token would then be passed along with any request back to your (hopefully, secure) server which validates the token and, if authorised, forwards the SMS request onto the SMS server.

How to hide harded coded password in Java source code

How to make sure that it will be impossible to recover the password ?

If it was impossible to recover, the program couldn't recover it either and it would be useless.

If it's impossible what to do to make the operation of recovering hard ?

Yes, don't call it password. Something very simple is,

String p = "kjasghfdkgasdfjlkasfljkahgdsfjhgdjsfh".substring(8, 15);

Or what the better way for my problem ?

Trust the people trying to help you. Give the account as limited access to do the work as possible and change the password regularly so what while they could work out, they won't have access for long.

What's the most secure way to embed a password inside Java code?

.... if you had to hard code it in the Java application, what measures could you take to make it harder to be fetched?

For a start, I would make damn sure that the person with management responsibility for making this bad decision is fully aware that this is fundamentally and irredeemably insecure1.

Then I'd probably think up some naff algorithm that assembles the password in an obscure way; e.g. by building two byte arrays and XORing them together ... and distributing obfuscated bytecodes. The best you can hope to do is to make it difficult for folks with limited skills to reverse engineer the password from your code.

(Encrypting the password with a strong algorithm won't help much, because the choice of algorithm and the decryption key both have to be embedded in your code. Indeed, any scheme you can dream of can be defeated by using a debugger to set a breakpoint at the point where the password needs to be in the clear.)

1 ... and that even Jon Skeet wouldn't be able to make it secure.


If some ways are better than others (for instance: JPasswordField stores the password in a char array instead of a String) ...

I just want to note that the normal reasoning for using a char array to hold passwords in JPasswordField and the like is to protect against bad guys reading passwords out of core dumps or swap files. It won't really help in this case because we have to assume that the bad guy you should be worried about is simeone with system admin access. He or she will have sufficient control to attach a debugger to the JVM and capture the bytes from the char array.



Related Topics



Leave a reply



Submit