When a company discloses a breach, the sentence that matters most to its users is often buried halfway down the notice: how the passwords were stored. "Hashed with bcrypt" and "hashed with unsalted MD5" describe the same theft with very different consequences. In the first case most users have time to change their passwords; in the second, a large share of those passwords can be recovered within days.
On the CompTIA Security+ SY0-801 exam, salting sits under objective 1.3 alongside hashing, and it is how you're expected to explain the right way to store passwords. This lesson covers why a plain hash isn't enough for passwords, what a salt and a pepper each add, why password hashing has to be slow, and how stolen hashes are attacked, so you can read a scenario about stored credentials and pick the control that fixes it.
A login system should store a verifier, never the password
A system that checks passwords doesn't need to know them. It needs a way to confirm that what the user typed matches what they chose at signup. There are three ways to store something for that check, and only one is right.
Plaintext storage is the worst case. Anyone who reads the database, a backup, a debug log or a support export has every password. Because people reuse passwords, a plaintext leak at one site turns into account takeovers at others.
Reversible encryption looks better but fails for the same reason in practice. The application has to hold the decryption key, usually on the same server as the database, so an attacker who gets one often gets both. Administrators can also decrypt the passwords, which no one should be able to do.
Hashing is the right model. At signup, the system hashes the password and stores the digest. At login, it hashes what the user typed and compares the two digests. The original password is never stored and never needed. This is also why a well-built "forgot password" flow resets your password instead of reminding you of it. A site that can email you your old password is storing it in a form it can reverse.
Fast, unsalted hashes fail in two predictable ways
Hashing passwords with a general-purpose algorithm like MD5 or SHA-256, and nothing else, still leaves two serious problems.
First, identical passwords produce identical hashes. Here is part of a user table where each password was hashed with plain SHA-256:
| User | Stored SHA-256 |
|---|---|
| alice | ed3c9886c697...043d1a |
| bob | d26f7dda816a...e37f19 |
| chen | ed3c9886c697...043d1a |
Anyone who reads the table can see that alice and chen chose the same password without knowing what it is. Recover one, and you have both. In a large user table, the most common hashes typically belong to passwords like 123456 and repeat hundreds of times.
Second, an unsalted hash of a given password is the same on every system in the world. That means attackers can compute hashes for billions of likely passwords once and reuse the results against every breach they ever obtain. Rainbow tables are a well-known form of this precomputation; they store chains of hashes so the tables take far less disk space, at the cost of some extra computation during each lookup. Against unsalted hashes, finding a common password becomes a lookup rather than a calculation.
Speed makes both problems worse. MD5 and SHA-256 were designed to be fast, because fast is what you want for checking a large file. A single modern graphics card can compute billions of these hashes per second, so working through every common password and its usual variations costs very little.
A real example of unsalted storage is Windows. The NT hash that Windows keeps for each local and domain account is an unsalted MD4 digest of the password, so two domain users with the same password have the same NT hash. That is one reason the domain controller's NTDS.dit database is treated as one of the most sensitive files in the enterprise.