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 scheme.
Because the challenge "What is your password?" is repeated, so is the response, opening the door to possible replay attacks.
Argon2's "i" variant is resistant to side-channel attacks, while "d" variant is resistant to time-memory tradeoff attacks. "id" is the hybrid variant, resistant to both and suitable for most purposes.
Make sure to use "id" variant of Argon2.
Salts are closely related to nonces, however, nonces are for communication protocols, not hashing. Nonces protect against replay attacks, while salts protect against precomputed hashes(aka rainbow tables). 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 output of 32 bytes will produce an unpredictable, and unique value.
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, right? That's because it is. One must be extremely careful implementing "peppers" because it can lead to length extension attacks and nasty problems with Bcrypt.
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.
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 function. Or even better, use Halite. Be sure to use PHP 7.2 and above. Take note that the default password hashing algorithm for PHP is Bcrypt.
Use Halite to hash then encrypt your passwords. An encryption key is required by the library for storing passwords.
Perhaps a third-party solution?:
- 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.
- 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 between the password and the identifier and decline passwords within a dangerous distance. Of course with a reasonable minimum distance.
Discourage passwords with personal information like names, dates, birthdays, etc. Or sharing them if at all possible.
- Protect your communications with TLS 1.2 and newer, the successor to the SSL 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, and spoofing phone numbers to obtain verification codes.
Single Responsibility Principle (or SRP) is one of the most important concepts in software development. The main idea of this concept is: all pieces of software must have only a single responsibility.