Proper Password Hashing: Salting, Key Stretching, and Secure Implementation
This article explains why simple password hashing is insufficient, describes common attacks such as dictionary, brute‑force, lookup‑table and rainbow‑table attacks, and provides best‑practice guidance—including random salts, CSPRNGs, key‑stretching algorithms like PBKDF2, bcrypt and scrypt—and complete PHP reference implementations.
0x00 Background
Most web developers need to design a user‑account system, and protecting passwords is the most critical part. Data breaches of large companies show that storing plain passwords is dangerous, so salted password hashing must be used.
0x01 Important Reminder
Do not write your own password‑hashing code; it is easy to get wrong. Use proven libraries such as phpass or the source code provided in this article.
0x02 What Is a Hash
A hash function is a one‑way function that converts arbitrary data into a fixed‑length fingerprint. Small changes in input produce large changes in output, making hashes suitable for password storage.
The typical registration/authentication flow is:
1. User creates an account.
2. Password is hashed (with a salt) and stored; no plaintext password is saved.
3. On login, the entered password is hashed with the same salt and compared to the stored hash.
4. If the hashes match, authentication succeeds.
5. Repeat step 3 for each login attempt.Never reveal whether the username or password was incorrect; use a generic error message to prevent user‑enumeration attacks.
Only cryptographic hash functions (e.g., SHA‑256, SHA‑512, RIPEMD, WHIRLPOOL) are suitable for password hashing; general‑purpose hash tables are not secure enough.
0x03 How Hashes Are Cracked
Dictionary and Brute‑Force Attacks
Attackers guess passwords, hash each guess, and compare to the target hash. Dictionary attacks use a list of common passwords; brute‑force tries every possible character combination.
Lookup Tables
Pre‑compute hashes for a large password dictionary and store them in a table for fast lookup. This is effective against unsalted hashes.
Reverse Lookup Tables
Attackers build a table of usernames and their hashes, then hash a common dictionary and compare, allowing them to discover which users reuse passwords.
Rainbow Tables
Rainbow tables trade computation time for storage space, enabling the cracking of many unsalted hashes with relatively small tables.
0x04 Adding Salt
Salting adds a random prefix or suffix to each password before hashing, ensuring that identical passwords produce different hashes and rendering lookup‑table attacks ineffective.
The salt does not need to be secret; it must be random and stored alongside the hash.
0x05 Incorrect Practices: Short Salt and Salt Reuse
Reusing the same salt for many passwords or using a short salt makes attacks feasible. Always generate a new, sufficiently long random salt for each password (e.g., 32 bytes for SHA‑256).
0x06 Incorrect Practices: Double‑Hashing and Exotic Hash Functions
Combining multiple hash functions or using non‑standard algorithms provides little security benefit and can introduce compatibility and safety issues. Use well‑tested standard algorithms instead.
0x07 Hash Collisions
Cryptographic hash functions are designed to make collisions computationally infeasible, but weak hashes like MD5 have known practical collisions. Prefer strong hashes such as SHA‑256, SHA‑512, RIPEMD, WHIRLPOOL, or SHA‑3.
0x08 Correct Approach: Proper Password Hashing
Basic: Salted Hash
Generate a salt with a cryptographically secure pseudo‑random number generator (CSPRNG) and hash the password+salt using a strong algorithm.
Platform
CSPRNG
PHP
mcrypt_create_iv, openssl_random_pseudo_bytes
Java
java.security.SecureRandom
.NET (C#, VB)
System.Security.Cryptography.RNGCryptoServiceProvider
Ruby
SecureRandom
Python
os.urandom
Perl
Math::Random::Secure
C/C++ (Windows API)
CryptGenRandom
Linux/Unix
/dev/random or /dev/urandom
Store both the salt and the resulting hash in the user record.
Storing a password:
1. Generate a long random salt using a CSPRNG.
2. Concatenate password and salt, then hash with a standard algorithm (e.g., SHA‑256).
3. Store the salt and hash in the database.Verifying a password:
1. Retrieve the stored salt and hash.
2. Concatenate the entered password with the same salt and hash.
3. Compare the computed hash with the stored hash; if equal, authentication succeeds.Key Stretching (Slow Hashes)
To defend against fast dictionary/brute‑force attacks, use a deliberately slow hash function such as PBKDF2, bcrypt, or scrypt, which performs many iterations and consumes CPU resources.
Typical parameters are chosen so that a single hash takes about half a second on the target hardware.
When using slow hashes in a web application, consider the impact on server load and possible denial‑of‑service attacks; you may lower the iteration count or add CAPTCHAs for suspicious login attempts.
0x09 Other Security Measures
Beyond hashing, protect the entire application: follow OWASP Top Ten, conduct regular security training, perform third‑party penetration testing, and monitor for intrusions.
0x0A Frequently Asked Questions
Which hash algorithms should I use?
PBKDF2, bcrypt, scrypt (key‑stretching)
Secure cryptographic hashes: SHA‑256, SHA‑512, RIPEMD, WHIRLPOOL, SHA‑3
Avoid outdated hashes like MD5 and SHA‑1, and avoid insecure crypt() variants.
How to handle password resets? Use a one‑time, random token bound to the user account, expire it quickly (e.g., 15 minutes), and never email plaintext passwords.
What if the user database is leaked? Notify users promptly, advise them to change passwords, and consider additional verification steps such as email confirmation.
0x0B PHP PBKDF2 Password‑Hashing Code
Below is a complete PHP implementation of PBKDF2‑based password hashing, including constant‑time comparison to mitigate timing attacks.
The slow_equals function uses XOR (^) to compare bytes without branching, preventing timing attacks.
By following the guidelines and using the provided reference implementation, developers can store passwords securely and mitigate common attack vectors.
Architect
Professional architect sharing high‑quality architecture insights. Topics include high‑availability, high‑performance, high‑stability architectures, big data, machine learning, Java, system and distributed architecture, AI, and practical large‑scale architecture case studies. Open to ideas‑driven architects who enjoy sharing and learning.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.