Every few months there's a breach announcement. Some company apologizes, tells users to reset their passwords, and adds, almost reassuringly, that passwords were "stored as hashes." The implication is that even though the database was stolen, the passwords themselves are safe.
Sometimes that's true. Often it's not. And the reason why gets at something genuinely interesting about how password cracking actually works.
What a Hash Actually Is
When you create an account and set a password, a well-designed system doesn't store what you typed. It runs your password through a hash function, stores the result, and throws the original away.
A hash function takes any input and produces a fixed-length output. Feed it correct-horse-42 and it spits out a string of characters. Feed it correct-horse-43 and it spits out something completely different. The output looks random, and crucially, you can't go backwards. There's no decryption key, no algorithm that takes the output and gives back the input. The math doesn't work that way.
So when you log in later, the system doesn't "decrypt" your password to check it. It hashes whatever you typed and compares that result to what's stored. If they match, you're in.
This is what makes hashing different from encryption. Encryption is a two-way process. Hashing is designed to be one-way. The stored value is a verifier, not a vault.
So here's the puzzle: if an attacker steals a database full of these verifiers, and they can't reverse them, how does anyone ever end up with the actual passwords?
Cracking Doesn't Mean Reversing
This is where most explanations go wrong. Password cracking isn't about mathematically reversing a hash. It's about guessing.
The attacker has your stored hash. They don't know your password, but they can make a guess. They take that guess, run it through the same hashing algorithm, and compare the result to what they stole. If the outputs match, they found your password. If not, they guess again.
That's it. The whole thing is a guessing game. Not a clever cryptographic attack, just guess-hash-compare, repeated as many times as possible.
The reason this works is that most people's passwords are predictable. Attackers don't guess randomly. They start with the most common passwords ever collected from previous breaches: 123456, password, qwerty, things like that. Then they work through dictionaries of real words in multiple languages. Then common substitutions: p4ssword, P@ssw0rd. Then names with birth years appended. Then everything else.
A password like football1 will be cracked in seconds, not because hashing is weak, but because football1 shows up early in every attacker's list. The problem isn't that the hashing algorithm is weak. It's that the password is so predictable that an attacker may find it after very few guesses.
A password like mT9$vqR2#nL7 is a different story. It's not in any list, not a variation of any word, not guessable in any reasonable order. An attacker might need to run through so many combinations that even with serious computing power, they'd never reach it in a useful timeframe.
Why Speed Is the Problem
Now here's where the algorithm choice matters.
SHA-256 is a cryptographic hash function you'll see mentioned constantly in security contexts. It's good at what it was designed for: verifying file integrity, signing data, building certificates. It's fast, which is usually desirable.
But fast is exactly the wrong property for password storage. SHA-256 is inexpensive to calculate, and that cost can be parallelised across modern hardware with ease. An attacker who steals a database of SHA-256-hashed passwords can work through enormous numbers of guesses very quickly.
This is why dedicated password-hashing algorithms exist. Argon2id, bcrypt, and scrypt are all designed to be slow and expensive on purpose. They force each guess to consume meaningful time or memory or both. That deliberate cost is the point: it makes guessing attacks significantly more expensive per attempt.
None of this makes cracking impossible. It just means that a password has to be guessed, and if the algorithm forces each guess to cost real time and memory, then cracking a long, uncommon password becomes impractical in any reasonable window.
Dedicated password-hashing algorithms raise the floor, but they don't build a ceiling. The attacker's resources still matter, and so does the password itself.
What a Salt Does (and Doesn't Do)
Every decent password-storage implementation also uses a salt: a unique random value generated for each account and combined with the password before hashing.
Two things happen as a result. First, if two users both choose hunter2 as their password, their stored hashes will look completely different because each one was combined with a different salt before hashing. An attacker can't look at the stolen database and immediately spot all the people who chose the same password.
Second, salts make precomputed attacks much less useful. One classic attack involves building a huge table of hashes for common passwords ahead of time, then looking up stolen hashes in that table. With unique salts, the attacker can't reuse any precomputed work. Every account has to be cracked individually.
But here's what a salt does not do: it doesn't stop offline guessing. The attacker still has the salt, because it's stored in the database right next to the hash. They still have the stolen verifier. They can still run their guess list through hash(salt + guess) for each account, one by one. The salt just means they can't recycle the work across accounts or use precomputed tables.
A salt combined with a slow algorithm is meaningful protection. A salt on top of SHA-256 is better than nothing but still leaves most predictable passwords exposed because the guessing is still cheap.
Why Some Passwords Survive and Others Don't
Length matters, but length alone isn't the whole story.
correcthorsebatterystaple is long, but if it became famous as a password example, it probably ends up in attacker wordlists eventually. Tr0ub4dor&3 has substitutions but follows a pattern that substitution rules will generate. P@ssword1! has uppercase, a symbol, a number, and hits every checkbox on a corporate password policy, and it would fall in seconds.
What actually makes a password hard to crack is whether an attacker would ever try it. Truly random passwords generated by a password manager are strong because they're not variations of anything, not in any list, not derivable from any pattern. An attacker can't guess in a useful order. They'd have to work through an astronomical space of possibilities with no way to prioritize.
Password reuse is a separate problem that bypasses hashing entirely. If someone uses the same password on five different services, and one of those services stores it in plaintext or with weak protection, the attacker doesn't need to crack any hash at all. They just try the recovered password on every other account associated with that email address.
What a Breach Actually Means
A company saying "passwords were hashed" is sometimes meaningful reassurance and sometimes just phrasing designed to sound better than it is.
What matters is which algorithm was used, how it was configured, and whether salts were applied properly. A database full of Argon2id hashes with appropriate parameters makes offline guessing significantly more expensive, which can meaningfully slow an attacker down -- though how much protection that provides in practice depends on password strength, how the algorithm was configured, and the attacker's resources. A database of unsalted MD5 hashes is essentially a list of passwords with a thin veil over it.
Even a well-implemented database represents a serious incident. The attacker now has the opportunity to run a guessing attack offline, without rate limits, without lockouts, without any of the controls that protect login forms. The only thing standing between the attacker and your users' passwords is the quality of the hashing implementation and the quality of the passwords themselves.
For developers, the practical conclusion is short: use a well-maintained library that handles password hashing through a modern algorithm, let it generate salts automatically, keep your cost parameters in line with current guidance, and never try to build your own scheme. The cryptography is not the part that needs creativity.
The reason database breaches keep resulting in cracked passwords isn't that hashing is fundamentally broken. It's that the combination of fast algorithms, predictable human choices, and years of accumulated breach data has made weak passwords genuinely indefensible. The math is fine. The passwords are the problem.
Top comments (0)