Password Storage
Store password in clear text (insecure)
- If the attacker successfully compromises the database, they gain access to all passwords, allowing them to log in as any user.
Store a password hash generated by common hashing algorithms(i.e. md5
, sha
) (insecure)
- If salt or randomness is not used, identical passwords will generate the same hash.
- As a result, an attacker can deduce that two users have the same password simply by comparing their matching hashes.
Store a salted
hash generated by fast
hashing algorithm (insecure)
- Here same password will generate two different hash outputs.
- However, salt is public therefore building the rainbow table is still possible if used fast hashing algorithms like sha3, blake.
Concept of Salt
- The concept is similar to personalize a hash function with a key / secret to create an authenticated tag / message authentication code (MAC).
- However, salt is not a
secret
means- We can store that in clear text. But it should not be accessible in public.
- We can store it in the same database but preferable separate table from password_hash.
- Salt should be generated from
CSRNG
(i.eos.urandom()
) function. - Salt should be unique per user. Thus, two passwords don’t produce the same hash.
- Salt should be as long as the output of the hash, minimum
32 bytes
.- If salt is too short, an attacker can again build the rainbow table for every possible salt.
- We cannot use
username
orUID
as salt because they may not be generated by a CSRNG and may be too short to meet the minimum 32-byte requirement.
- Instead of simply appending or prepending the salt to the password, it’s recommended to use a proper hashing function that accepts the salt as a
dedicated parameter
. ⠀Is there any issue with prepending the salt to the password and using the SHA-2 algorithm?
- A hash length extension attack isn’t applicable here, since the attacker doesn’t need to compute a valid hash by guessing the salt length.
- Instead, the attacker’s focus would be on recovering the original password from the known hash.
- The main concern lies in the fact that SHA-2 is a fast hashing algorithm, making it easier for attackers to quickly generate rainbow tables.
Is there any performance impact if we use slow hashing functions.
Yes. this intentional slow down can have an impact on the performance of an application, especially during the authentication process when users are logging in and their passwords need to be hashed and compared against stored hashes.
To mitigate the performance impact, many applications use techniques such as parallelism, multi-threading, or specialized hardware to help mitigate the slowdown caused by slow hashing algorithms.
Additionally, using appropriate
work factor parameters
when configuring the hashing algorithm can allow you to strike a balance between security and performance.
Concept of Pepper
- A pepper is a
secret
added (prepend
/append
) to the password during hashing. - This value should differs from a salt and that it is not stored alongside a password hash, but rather the pepper is kept separate in some other medium, such as a Hardware Security Module.
- NIST recommends size minimum 16 byte.
- It should be unique per application.
- Should be generated from
CSRNG
function.
Password Hashing Algorithms
09/2024: All key derivation functions listed below are resistant to
rainbow table
attacks.
PB-KDF2 - (Not Recommended anymore)
- It is NOT resistant to
- GPU attacks (parallel password cracking using video cards) and to
- ASIC attacks (specialized password cracking hardware).
- Mostly known for using as KDF not password storage.
- Flexibility to specify the hashing algorithm as argument.
- It is NOT resistant to
Bcrypt
- Less resistant to ASIC and GPU attacks.
- Although it provides
configurable iterations count
, but usesconstant memory
,- so it is easier to build
hardware-accelerated password crackers
.
- so it is easier to build
- Salt is
pre-pended
with digest therefore it easily known by someone who has the hash. - The bcrypt algorithm only handles passwords up to 72 characters, any characters beyond that are ignored.
- To work around this, a common approach is to hash a password with a cryptographic hash (such as sha256) and then base64 encode it to prevent NULL byte problems before hashing the result with bcrypt.
- It also can be used as KDF / key generation.
Scrypt - (Recommended)
It is
memory-intensive
, designed to prevent GPU, ASIC and FPGA(highly efficient password cracking hardware) based attacks.- Supports XOF.
- Cons: Cutdown version means shorter hash outputs are prefixes of longer hash outputs.
- Function Arguments are
1
2
3
4
5
6
7
8
9
hash_key = Scrypt(password, salt, N, r, p, derived-key-len)
config parameters are:
1. password => the input password (8-10 chars minimal length is recommended)
2. salt => securely-generated random bytes (64 bits minimum, 128 bits recommended)
3. p => parallelism factor (threads to run in parallel, affects the memory, CPU usage), e.g. 1
4. n => iterations count (affects memory and CPU usage), e.g. 16384 or 2048, must be power of 2
5. r => block size (affects memory and CPU usage), e.g. 8
6. derived-key-length => how many bytes to generate as output, e.g. 32 bytes (256 bits)
Argon2 - (Recommended)
- Strong resistant to GPU, ASIC and FPGA attacks.
- Argon2 config parameters are very similar to Scrypt.
- API -
argon2_cffi
lib also provides function that supports inbuilt random salt generation. - The Argon2 function has several variants:
- Argon2d – provides strong GPU resistance, but has potential side-channel attacks (possible in very special situations).
- Argon2i – provides less GPU resistance, but has no side-channel attacks.
- Argon2id – recommended (combines the Argon2d and Argon2i).
Summary: How to store and verify password
- Use argon2 / scrypt.
- Use
pepper
along with thesalt
to add defense in depth. - Avoid directly comparing two hashes as strings. If the application lacks rate limiting or CAPTCHA protection, it leaves the door open for attackers to execute a timing attack to guess the password.
- Solution: XOR two hashes and compare with 0 or 1.
- python3: Use compare_digest(good_sig, sig) method from
hmac
lib.
1
compare_result = hmac.compare_digest(str,str)
Code Repo
https://github.com/greyshell/python_programming/tree/master/crypto/password_hashing