If someone steals your database, what do they find in the password column?
| What you stored | What the thief gets |
|---|---|
| The password itself | Every user’s real password |
| An unsalted fast hash (MD5, SHA-1, SHA-256) | Most passwords, cracked in minutes, using ready-made tables and fast computers |
| A slow, salted password hash | Very little. Each password must be guessed one at a time, slowly |
The last row is the goal.
Hashing vs encryption
- Encryption is two-way: you can lock and open the data with a key.
- Hashing is one-way: you turn the data into a fixed-size fingerprint and cannot go back.
For passwords you want hashing. You never need to know the password. You only need to check whether the typed one matches.
<?php
echo hash("sha256", "secret") . "\n";
echo hash("sha256", "secret");
2bb80d537b1da3e38bd30361aa855686bde0eacd7162fef6a25fe97bf527a25b
2bb80d537b1da3e38bd30361aa855686bde0eacd7162fef6a25fe97bf527a25b
The same input always gives the same hash. That is also why plain sha256 is bad for passwords: an attacker can pre-compute hashes for millions of common passwords and just look yours up.
Salt
A salt is random data added to each password before hashing. With a salt, two users who choose secret get completely different hashes, and pre-computed tables become useless. The salt is stored together with the hash.
Slow on purpose
MD5 or SHA-256 can be computed billions of times per second on a graphics card. A good password hash is deliberately slow (and uses memory), so an attacker can only try a few guesses per second. bcrypt and Argon2 do this.
Never write this yourself
Do not invent your own scheme (md5($password . $salt), double hashing, a secret “pepper” in a custom formula). Cryptography is easy to get wrong. PHP has the right tool built in, and it handles salt, cost and format for you:
password_hash()creates the hash.password_verify()checks a password against it.password_needs_rehash()tells you when to upgrade old hashes.
<?php
$hash = password_hash("secret", PASSWORD_DEFAULT);
var_dump(password_verify("secret", $hash));
var_dump(password_verify("Secret", $hash));
bool(true)
bool(false)
Things you should not use for passwords
| Function | Why not |
|---|---|
md5(), sha1(), sha256 and friends | Too fast, no salt handling |
crypt() directly | Easy to misuse |
| Your own “encryption” | Reversible, so a leak exposes every password |
password_hash with a hand-made salt option | The salt option was removed. PHP picks a safe salt itself |
If you already have old hashes
Users with an old MD5 hash can be migrated: verify with the old method at their next login, then immediately re-hash with password_hash() and save the new value. password_verify() shows password_needs_rehash().
What hashing does not do
Hashing protects the passwords if the database leaks. It does not stop someone from guessing a weak password on your login form. You still need rate limiting, strong password rules and (for important accounts) two-factor authentication. See Authentication Security.
Why this matters in WordPress and Laravel
wp_users (using its own portable hashing, and newer versions use bcrypt). Laravel hashes with bcrypt or Argon2 through its Hash facade. In both, you never see a plain password in the database.