Securing a Password in Source Code

Securing a password in source code?

Don't store you password in your source code, store it in a protected section within you App.Config (or Web.Config).

See Encrypting Configuration File Sections Using Protected Configuration section in this Microsoft Doc

This works by encrypting the encryption keys using built-in Windows stuff, locked to the MAC address and various other undocumented things.

This will even work if you are using more than one server:

... if you are planning to use the same encrypted configuration file on multiple servers, such as a Web farm, only the RsaProtectedConfigurationProvider enables you to export the encryption keys used to encrypt the data and import them on another server.

Using this, if someone wanted to get your password, they would have to first break the Windows security on your server (not impossible, but harder than looking into your IL for the password by far).

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.

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.

Securely store a password in program code?

There is no point in symmetrically encrypting with a string that's hard-coded into your executable. It will only give a false sense of security. No amount of hashing fixes this scheme.

See this Pidgin FAQ for the same point in a different context.

I am unclear why you think you need the inter-app communication to be encrypted. If this communication is local to the machine, then I don't see the need for encryption, particularly encryption that isn't user-specific. Is this a DRM scheme?

EDIT: If it's being passed to a different machine, perhaps you can hard-code a public key, and then have the other machine decrypt with the matching private key.

Is It Secure To Store Passwords In Web Application Source Code?

It appears the answer is the following:

  1. Don't put credentials in source code but...
  2. Put credentials in a configuration file
  3. Sanitize log files
  4. Set proper permissions/ownership on configs

Probably more depending on platform...

Encrypt password in C source code

String literals are usually retained and stored somewhere in the binary even in compiled C source.

What you could do is a similar method to how (properly implemented) web apps validate login information where that info is stored in a database. Just store the password in hashed form. Often, the method is to use MD5 + salt (here is a description and some sample PHP code). What you can do is instead of transmitting or storing the plaintext password, just hash the user input and check the hash against the stored hash value. Matching hashes corresponds to matching passwords.

EDIT

This won't help with the FTP server case though, since you cannot modify its source code...

How do I properly protect a password when the source code is available?

Side note: I assume you are doing this server-side (nodejs?). Your implementation is non-standard and your terminology is mixed up (the normal terminology has "salt" as a value that changes every time, whereas "pepper" is a fixed server side string), but it does have the main concepts in place. I'd recommend though that you would use a standard "password hashing" algorithm such as scrypt or bcrypt.

In regard to your question "Couldn't this malicious person just run all possible combinations of 8 characters (minimum password length) through my function and see what value of password creates the correct hash ?", the answer is yes, but it's going to take him a long time to do that. That's the entire purpose of your rehash variable: to slow the user down. See: Our password hashing has no clothes for more details.

How can I safely store a password in source code?

Ideally you should not be storing the password in your source code which runs on client machine.

For this type of scenario, still you can avoid hard coding by retrieving the password from server using some API calls after performing the required authentication/ authorization.

If you store the password in the code running on the client side, whatever you do like obfuscation/ encryption/ encoding, it can still be extracted using reverse engineering.

Edit:

Thick client - all logic is on client side. No hosting server. It
would be exaggerated to rent a server just for protecting my email
password. Is there really no other solution? How is connection string
to database then secured in other applications against reverse
engineering?

As mentioned above you can't completely secure it, but you can make it tough to break, for that you can try following steps.

  1. Encrypt your password.
  2. Store the encryption key in multiple parts in different location,
    like one part in code file, one part in configuration file and one
    part in user registry.
  3. Obfuscate your code.


Related Topics



Leave a reply



Submit