Safely Create and Store Passwords
Nearly every time when it comes to user profiles it is necessary to manage user credentials and thus be able to create and store user passwords. It should be common practice to use hashed and salted passwords to be prepared for database disclosure and hash reversing by the use of rainbow tables.
However, it is (sadly) not uncommon to find passwords stored in plaintext (we’ll skip a list of big players who had to learn how to do it right the hard way). The consequences when a database with unencrypted, unhashed and unsalted passwords is leaked should be obvious….. The second worst way to do it is to use hashed, but unsalted passwords. In this case rainbow tables or hash reversing services on the web like this or this are of tremendous help. And finally, the third worst way is to rely on encrypted records alone – once the key or the decrypted database gets leaked the game is over!
So how to do it right? The easy answer is: use PBKDF2WithHmacSHA1 together with a salt value. An example how to use it can e.g. be found here. This implementation looks mature, but complicated. If you simply want to get a feeling for the concept of salting passwords, you may want to look at the following demo code:
public static final String HASH_ALGORITHM = "SHA-256"; public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8"); private static final char[] PASSWORD_CHARS = new char[]{ '!', '@', '#', '$', '%', '&', '*', '(', ')', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'}; public String getRandomString(final int length) { /* * Don't try to seed SecureRandom yourself unless you know * what you are doing! * @see Randomly failed! Weaknesses in Java Pseudo Random Number Generators (PRNGs). */ SecureRandom secureRandom = new SecureRandom(); StringBuilder sb = new StringBuilder(length); int position = 0; // create a random string of the requested length from a set of allowed chars for (int i = 0; i < length; i++ ) { position = secureRandom.nextInt(PASSWORD_CHARS.length); sb.append(PASSWORD_CHARS[position]); } return sb.toString(); } public static byte[] createPasswordHash(final String password, final String salt) { byte[] result = null; try { MessageDigest digest = MessageDigest.getInstance(HASH_ALGORITHM); digest.update(salt.getBytes(DEFAULT_CHARSET)); digest.update(password.getBytes(DEFAULT_CHARSET)); result = digest.digest(); } catch (NoSuchAlgorithmException e) { // TODO Logging } return result; } public static boolean checkPassword(final User user, final String password) { boolean result = false; String storedPasswordHash = user.getPwHash(); String salt = user.getSalt(); byte[] checkPasswordHashBytes = createPasswordHash(password, salt); String checkPasswordHash = encodeBase64(checkPasswordHashBytes); // for simplicity let's say we use Base64 if (checkPasswordHash != null && storedPasswordHash != null && checkPasswordHash.equals(storedPasswordHash)) { result = true; } return result; }
The code expects some kind of User object with pwHash and salt fields (both fields are not sensitive!) to store the necessary information. This User object could (in case there is no other sensitive data linked to the object) be safely persisted. Even if the database gets leaked an attacker must either brute force the password-salt combination or compute a rainbow table for the salted password. Please note that this rainbow table could not be reused for the same password with a different salt! This means that with a randomly chosen salt an attacker will need rainbow tables to reverse the hash function for every salt – even if the password stays the same!
The code above is kept as simple as possible. For example getRandomString can be reused for creating the salt value and maybe the generation of a temporary password during the registration process. But remember, this code is far from being useable in production environments!
Some final notes: Be sure to use a salt that is long enough, prevent reuse of salts and use strong algorithms for hashing!
Why not use http://commons.apache.org/proper/commons-lang/apidocs/org/apache/commons/lang3/RandomStringUtils.html with SecureRandom passed explicitly?
No, no, and no. Please don’t use SHA-(1|256|512) for hashing passwords.
Please, for the love of your users, use a hash algorithm specifically created for hashing credentials, like bcrypt, scrypt, or PBKDF2.
PBKDF2 is a decent algo, but it requires very little RAM, so rainbow tables can be generated with comparative ease on GPU’s. Scrypt can use large amounts of both CPU and RAM, and hence is more resilient to GPU-based attacks.
http://www.tarsnap.com/scrypt/scrypt.pdf
Additionally, the salt can be almost anything, including user’s id or name. The salt does not need to be secret or random, the point of a per-user salt is that an attacker would need to generate a rainbow table per user.