And specifically about how as a developer, you should protect the passwords of your users.
I think there can't be enough emphasis of how important it is to keep the password of your users safe.
So, instead of 'lecturing' about why you should do it, lets dive in right away into how you do it.
So, given the fact you have different users on your system, and each of them has a password,
we would like to keep their passwords safe.
This translates to storing their passwords somewhere (a DB for example), encrypted.
So before we really give code example how it's done, a small note on encryption:
When you pick an encryption algorithm to apply on your passwords, try to think if you ever
plan on decrypting them. In most cases the answer is 'NO'.
(Picking a symmetrical algorithm also means that in case an attacker finds out the key,
they can use it to decrypt the passwords).
Thus, we'll drop all symmetrical algorithms such as AES(Rijndael), RSA based, etc.. and pick
an asymmetrical encrypting algorithm such as hashing.
Hashing algorithms such as SHA are asymmetrical-key algorithms, meaning that for an attacker
that put their hands on the encrypted password it is impossible to recover the original password.
Different inputs for SHA can yield the same output, but with a very low probability.
Now that we understand why we should use a Hashing algorithm, we can explain the steps for
encrypting and give an example code how it's done in Java.
Encryption recipe:
1. Take password and salt* as an input.
2. Apply some hashing algorithm on the input. (We will choose SHA-256)
3. Repeat step 2 a large amount of times where the input to the algorithm is the last output of the encryption.
*salt - Is an additional fixed input for adding complexity for the encrypted password.
Lets write this in Java:
package nm.example;
import org.apache.commons.codec.binary.Base64;
import java.security.MessageDigest;
public class PasswordEncrypter {
private final static int NUMBER_OF_HASHING = 10000 ;
private final static String CHARSET = "UTF-8" ;
private final static String ENCRYPTING_ALGORITHM = "SHA-256";
/**
* Encrypts a given password.
*/
public static String encryptPassword(String salt, String password) throws Exception {
// Transform the given password & salt to Base64 in order
// to work on a closed set of 64 characters instead of Unicode.
Base64 base64EncoderDecoder = new Base64();
String b64password = base64EncoderDecoder.encodeAsString(password.getBytes(CHARSET)) ;
// Create the salt & transform it to Base64.
String b64Salt = base64EncoderDecoder.encodeAsString(salt.getBytes(CHARSET));
// The encryption!
byte[] proposedDigest = getHash(b64password, b64Salt);
return base64EncoderDecoder.encodeAsString(proposedDigest) ;
}
/**
* From a password, a number of iterations and a salt,
* returns the corresponding digest
*/
private static byte[] getHash(String password, String salt) throws Exception {
MessageDigest digest = MessageDigest.getInstance(ENCRYPTING_ALGORITHM);
digest.reset();
digest.update(salt.getBytes(CHARSET));
byte[] passwordBytes = password.getBytes(CHARSET);
byte[] input = digest.digest(passwordBytes);
for (int i = 0; i < NUMBER_OF_HASHING; i++) {
digest.reset();
input = digest.digest(input);
}
return input;
}
}
So the output for: import org.apache.commons.codec.binary.Base64;
import java.security.MessageDigest;
public class PasswordEncrypter {
private final static int NUMBER_OF_HASHING = 10000 ;
private final static String CHARSET = "UTF-8" ;
private final static String ENCRYPTING_ALGORITHM = "SHA-256";
/**
* Encrypts a given password.
*/
public static String encryptPassword(String salt, String password) throws Exception {
// Transform the given password & salt to Base64 in order
// to work on a closed set of 64 characters instead of Unicode.
Base64 base64EncoderDecoder = new Base64();
String b64password = base64EncoderDecoder.encodeAsString(password.getBytes(CHARSET)) ;
// Create the salt & transform it to Base64.
String b64Salt = base64EncoderDecoder.encodeAsString(salt.getBytes(CHARSET));
// The encryption!
byte[] proposedDigest = getHash(b64password, b64Salt);
return base64EncoderDecoder.encodeAsString(proposedDigest) ;
}
/**
* From a password, a number of iterations and a salt,
* returns the corresponding digest
*/
private static byte[] getHash(String password, String salt) throws Exception {
MessageDigest digest = MessageDigest.getInstance(ENCRYPTING_ALGORITHM);
digest.reset();
digest.update(salt.getBytes(CHARSET));
byte[] passwordBytes = password.getBytes(CHARSET);
byte[] input = digest.digest(passwordBytes);
for (int i = 0; i < NUMBER_OF_HASHING; i++) {
digest.reset();
input = digest.digest(input);
}
return input;
}
}
PasswordEncrypter.encryptPassword("secret-salt", "my-password")
is "NiNP/e/NOzmoCQGaShXENFzpYXv7+EclH9st+dnfBWE="
As you can see I'm using the Apache Commons Codec library for Base64 encoding,
which is available at: http://commons.apache.org/codec/download_codec.cgi
Happy encrypting.