Programs that store a password as cleartext (unencrypted text data) risk exposure of the password in a variety of ways. Programs must prevent this information from being leaked. Although programs generally receive passwords from users as cleartext, this they should be the last time the password is in this formensure that the passwords are not stored as cleartext.
An acceptable technique for limiting the exposure of passwords is the use of hash functions, which allow programs to indirectly compare an input password to the original password string without storing a cleartext or decryptable version of the password. This approach minimizes the exposure of the password without presenting any practical disadvantages.
...
The value produced by a hash function is the hash value or message digest. Hash functions are computationally feasible functions whose inverses are computationally infeasible. In practice, a password can be encoded to a hash value, but decoding remains infeasible. The equality of the passwords can be tested through the equality of their hash values.
You should always Always append a salt to the password you are hashingbeing hashed. A salt is a unique (often sequential), or randomly generated piece of data that is stored along with the hash value. The use of a salt helps prevent brute-force attacks against the hash value, provided the salt is long enough to generate sufficient entropy (shorter salt values cannot significantly slow down a brute-force attack). Each password should have its own salt associated with it. If a single salt were used for more than one password, two users would be able to see whether their passwords are the same.
The choice of hash function and salt length presents a trade-off between security and performance. Increases in the time required to compute hash values raise the effort required for effective brute-force attacks, but make the program slower when must it validate validating a password. Increasing the length of the salt makes brute-force attacks more difficult, but requires additional storage space.
...
Code Block | ||
---|---|---|
| ||
public final class Password { private void setPassword(byte[] pass) throws Exception { bytes[] encrypted = encrypt(pass); // arbitrary encryption scheme clearArray(pass); saveBytes(encrypted,"password.bin"); // encrypted password to password.bin } private boolean checkPassword(byte[] pass) throws Exception { boolean arrays_equal; byte[] encrypted = loadBytes("password.bin"); //load the encrypted password byte[] decrypted = decrypt(encrypted); arrays_equal = Arrays.equal(decrypted, pass); clearArray(decrypted); clearArray(pass); return arrays_equal; } private clearArray(byte[] a) { //set all of the elements in a to zero } } |
...
Code Block | ||
---|---|---|
| ||
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public final class Password {
private void setPassword(String pass) throws Exception {
byte[] salt = generateSalt(12);
MessageDigest sha_1 = MessageDigest.getInstance("SHA-1");
byte[] hashVal = sha_1.digest((pass+salt).getBytes()); //encode the string and salt
saveBytes(salt, "salt.bin");
saveBytes(hashVal,"password.bin"); //save the hash value to credentials.bin
}
private boolean checkPassword(String pass) throws Exception {
byte[] salt = loadBytes("salt.bin");
MessageDigest sha_1 = MessageDigest.getInstance("SHA-1");
byte[] hashVal1 = sha_1.digest((pass+salt).getBytes()); //encode the string and salt
byte[] hashVal2 = loadBytes("password.bin"); //load the hash value stored in password.bin
return Arrays.equals(hashVal1, hashVal2);
}
private byte[] generateSalt(int n) {
// Generate a random byte array of length n
}
}
|
...
Code Block | ||
---|---|---|
| ||
import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; public final class Password { private void setPassword(byte[] pass) throws Exception { byte[] salt = generateSalt(12); byte[] input = appendArrays(pass, salt); MessageDigest sha_1 = MessageDigest.getInstance("SHA-1"); byte[] hashVal = sha_1.digest(input); //encode the string and salt clearArray(pass); clearArray(input); saveBytes(salt, "salt.bin"); saveBytes(hashVal,"password.bin"); //save the hash value to credentials.pw } private boolean checkPassword(byte[] pass) throws Exception { byte[] salt = loadBytes("salt.bin"); byte[] input = appendArrays(pass, salt); MessageDigest sha_1 = MessageDigest.getInstance("SHA-1"); byte[] hashVal1 = sha_1.digest(input); //encode the string and salt clearArray(pass); clearArray(input); byte[] hashVal2 = loadBytes("credentials.pw"); //load the hash value stored in credentials.pw return Arrays.equals(hashVal1, hashVal2); } private byte[] generateSalt(int n) { // Generate a random byte array of length n } private byte[] appendArrays(byte[] a, byte[] b) { // Return a new array of a appended to b } private void clearArray(byte[] a) { // set all of the elements in a to zero } } |
In both the setPassword()
and checkPassword()
methods, the cleartext representation of the password is erased immediately after it is converted into a hash value. Consequently, attackers must work much harder to retrieve the cleartext password after the erasure. Providing truly guaranteed erasure is extremely challenging, likely to be platform-specific and may even be impossible due to the because of the possible involvement of copying garbage collectors, dynamic paging, and other platform features that operate below the level of the Java Language. Note, however, that most other languages share these complications (with the possible exception of garbage collection).
...