I’ve been doing a lot of reading on cryptography lately. Some of my goals require a deep knowledge of encryption and how to implement successfully, so diving in now is an ideal time.

One of the first updates I decided to do, in order to strengthen the banking project, was to update the password storage. I took a lead from the OWASP guidelines:

  • Use a unique salt per password
  • Have a sane, long minimum for password length
  • Use an expensive cryptography function for hash calculation

Argon2

At the top of the list of recommended hashing functions was Argon2. There are various factors that make this the most recommended, not least the duration required to calculate the hash. The longer the duration required, the more impractical it becomes in order to brute force passwords.

I found a great implementation of Argon2 . It’s super easy to use so implementing this was a breeze. The code below shows the new implementation:

// Generate hash
userPasswordSalt := userSalt + clearTextPassword

hashOutput, err := argon2.Key([]byte(userPasswordSalt), []byte(Config.PasswordSalt), 3, 4, 4096, 64, argon2.Argon2i)
if err != nil {
    return "", errors.New("appauth.CreateUserPassword: Could not generate secure hash. " + err.Error())
}

userHashedPassword := hex.EncodeToString(hashOutput)

Unique salts per user, sane min length

The second factor was having a unique salt per user. I used the same Argon functionality above to generate this salt from a random string. Both the salt and the passwords end up being 128 characters long. If storage becomes a problem this will be revisited.

The minimum password length was also trivial to implement. This is set as a constant in the appauth subpackage and checked like so:

// Check password length
if len(clearTextPassword) < MIN_PASSWORD_LENGTH {
	return "", errors.New("appauth.CreateUserPassword: Password must be at least " + string(MIN_PASSWORD_LENGTH) + " characters")
}

Making tests pass

After the implementation of the above, I went through the tests to make sure they passed. There were a few changes but all in all minor. All tests pass and the benchmarks are slower, which is the desired outcome.

Conclusion

These small changes will make a massive difference to the strength of the passwords. This quote made me think differently about password storage:

Design password storage assuming eventual compromise

I had always tried to think like this, but the points listed put it in a new light.

As always the code is on Github with this particular code in this pull request.