How to Process Passwords as a Software Developer
Tags: security, cryptography
Published: 2019-10-20 01:09:20

Passwords are still the primary method of authentication today, in a form of *something you know.* Humans are lazy, often resulting in low entropy and reused passwords.

Magnitudes of research and thought have been put into protecting passwords. They are known as the simplest form of a [challenge–response authentication](https://en.wikipedia.org/wiki/Challenge%E2%80%93response_authentication) scheme.

Because the challenge "What is your password?" is repeated, so is the response, opening the door to possible [replay attacks](https://en.wikipedia.org/wiki/Replay_attack).


## Choose the Right Hashing Algorithm

[Argon2](https://en.wikipedia.org/wiki/Argon2) is a [key derivation function](https://en.wikipedia.org/wiki/Key_derivation_function), the winner of the [password hashing competition](https://password-hashing.net/) and should be used for new projects. In case it isn't available, use [Scrypt](https://www.tarsnap.com/scrypt.html). Any other KDF is nonoptimal.

Argon2's "i" variant is resistant to [side-channel attacks](https://en.wikipedia.org/wiki/Side-channel_attack), while "d" variant is resistant to [time-memory tradeoff attacks](https://en.wikipedia.org/wiki/Time/memory/data_tradeoff_attack). "id" is the hybrid variant, resistant to both and suitable for most purposes.

**Make sure to use "id" variant of Argon2.**


### Salt Hashes Properly

[Salts](https://en.wikipedia.org/wiki/Salt_(cryptography)) are closely related to [nonces](https://en.wikipedia.org/wiki/Cryptographic_nonce), however, nonces are for *communication protocols,* not hashing. Nonces protect against replay attacks, while salts protect against precomputed hashes(aka [rainbow tables](https://en.wikipedia.org/wiki/Rainbow_table)). It's important to separate them, as they aren't protecting against the same things.

The only requirements of salts are to be **unique to each hash and public,** however, they *should be unpredictable to* prevent precomputing future hashes.

A [cryptographically secure pseudo-random number generator](https://en.wikipedia.org/wiki/Cryptographically_secure_pseudorandom_number_generator) output of 32 bytes will produce an unpredictable, and unique value.


### "Pepper" Hashes Properly

If you've been around the internet, researching password salts, you've probably heard of the "pepper". It is a cryptographic secret(secret key), that must be unpredictable and unique to each application, used in a hash function.

Wait ... sounds a lot like an [keyed-hash message authentication code](https://en.wikipedia.org/wiki/HMAC), right? That's because it is. One must be extremely careful implementing "peppers" because it can lead to [length extension attacks](https://en.wikipedia.org/wiki/Length_extension_attack) and [nasty problems with Bcrypt.](https://blog.ircmaxell.com/2015/03/security-issue-combining-bcrypt-with.html)

Argon2 actually implements a `secret value` in the official spec as an optional argument to act as a "pepper". If you're using an implementation that does not support the `secret value`, **encrypt the hash.** If you're not using Argon2, **encrypt the hash.**

Do not waste your time implementing both a "pepper" and encrypting the hash. You gain no practical benefit. And you might want to store your cryptographic secrets in a [hardware security module.](https://en.wikipedia.org/wiki/Hardware_security_module)


## Libraries & Services

**You SHOULDN'T be rolling your own authentication system in production, use pre-made libraries and services. But you should learn how to implement them and how they work.**

If you're a PHP developer, use the built-in [`password_hash`](https://www.php.net/manual/en/function.password-hash.php) function. Or even better, use [Halite.](https://github.com/paragonie/halite) Be sure to use PHP 7.2 and above. Take note that the default password hashing algorithm for PHP is [Bcrypt.](https://en.wikipedia.org/wiki/Bcrypt)

Use Halite to hash then encrypt your passwords. An encryption key is required by the library for storing passwords.

```php
PargonIE\Halite\Password::hash($_POST["password"], $encryptionKey);
```

[Another great post about how you should hash passwords in many different programming languages.](https://paragonie.com/blog/2016/02/how-safely-store-password-in-2016)


Perhaps a third-party solution?:

* [SimpleLogin](https://www.simplelogin.io/)
* [Okta](https://www.okta.com/)
* [OpenID](https://en.wikipedia.org/wiki/OpenID)


## Use a Reasonable Policy

* Maximum length of *no less* than 128 characters.
* Minimum length of 12 or 16 characters.
* Support almost if not all Unicode and whitespace.
* Decline known passwords via [HIBP API](https://haveibeenpwned.com/API/v3).
* Decline passwords matching the identifier, e.g: email, username.
* Don't enforce special characters, uppercase, lowercase, symbols, etc.
* Don't truncate, sanitize, or format their passwords in any way shape or form!
* Don't prevent them from copying and pasting into the password fields.
* Don't limit what they can or cannot put into the password fields.
* Don't require password changes every so often.

What matters most, is the length(more so) and entropy. If you feel still inclined to enforce the character set above, don't. **Enforce multi-factor authentication instead,** their accounts will be far better with it.

If you enforce that character set, you run the risk of them writing it down, forgetting it, annoying them, or choosing a weak password. You can also cause users to write down their passwords if you enforce password changes.

As pointed out by @nylen, perhaps calculate the [Levenshtein distance](https://en.wikipedia.org/wiki/Levenshtein_distance) between the password and the identifier and decline passwords within a dangerous distance. Of course with a reasonable minimum distance.


### Finally Encourage Good Practices

Encourage the use of [diceware](http://world.std.com/~reinhold/diceware.html), [passphrases](https://www.useapassphrase.com/), password managers (like [BitWarden](https://bitwarden.com/)), and multi-factor authentication([FIDO](https://fidoalliance.org/) and [TOTP](https://en.wikipedia.org/wiki/Time-based_One-time_Password_algorithm) protocols).

Discourage passwords with personal information like names, dates, birthdays, etc. Or sharing them if at all possible.


## Notes

* Protect your communications with [TLS 1.2 and newer,](https://en.wikipedia.org/wiki/Transport_Layer_Security) the successor to the [SSL](https://en.wikipedia.org/wiki/Transport_Layer_Security#SSL_1.0,_2.0,_and_3.0) protocol. Otherwise sensitive information(passwords) will be exposed, rendering your state-of-the-art hashing useless.
* **Do not implement SMS for multi-factor authentication!** They're easily exploitable via [SIM swap scam](https://en.wikipedia.org/wiki/SIM_swap_scam), and spoofing phone numbers to obtain verification codes.