Your Password Has Been Leaked

Why password leaks happen, how they're exploited, and how to protect yourself—an interactive guide.

Part 1: Imagine you're designing a login system

You're building an app where every user has a private account. How do you stop people from accessing each other's? You need a way to identify users without handing out the keys.

Usernames alone aren't enough—anyone who knows or guesses a username could claim the account. You need something that proves the person at the keyboard is the owner, without exposing a long‑lived secret. Email codes and magic links do that: the service sends a one‑time link or code to the user's inbox; only someone with access to that email can log in. No password is stored. It's a solid approach, but it depends on email and adds friction. The simpler, more common choice is passwords.

With passwords, every account has two sides:

  • Username (the public side) — How the system identifies the account: an email, a handle, or an ID. It's not secret; think of it as the label on a locker.
  • Password (the private side) — A secret only the user—and the system—should know. It's the key. Whoever has it can act as that user.

So an account is the pair username + password. The system stores both; on login, it checks that what the user typed matches what it has on file. The next question: how is that actually stored?

How it's done in a database

A naive approach is to store exactly what the user signs up with: username and password, as‑is. Plaintext means the password is stored in readable form—no scrambling, no one‑way transformation. What you type is what's saved.

usernamepassword
alicealice123
bobqwerty
carolpassword1

In theory this could work: only you and your app can access the database, so accounts should be safe. That's naive security—betting that the database will never fall into the wrong hands.

Now attackers have your users' plaintext passwords. They can log into those accounts on your system—and worse, reuse the same credentials elsewhere. People often use the same password on many sites. Attackers take leaked username/password pairs and try them on other services: email, dating apps, social networks, banking. That's credential stuffing: reusing leaked credentials at scale. One leak, many accounts compromised, because the passwords were stored in plaintext.


Part 2: Hashing — storing a fingerprint, not the key

We just watched the naive developer's downfall. How do we avoid it? Don't store the password—store a fingerprint of it. You can't turn a fingerprint back into a hand; a good hash of a password shouldn't let you recover the password. Store the hash; on login, hash what the user types and compare. Match means correct password.

What is hashing?

A hash function takes any input and spits out a fixed‑length string (the hash). What matters:

  • Deterministic — Same input always gives the same hash.
  • One‑way — You can't practically reverse it; given a hash, you shouldn't get back the input.
  • Fixed‑length — One character or a whole book, the hash is the same length (e.g. 64 hex digits for SHA‑256).
  • Avalanche effect — Change one bit of input and the hash changes completely. Similar inputs don't give similar hashes.

Try it below: type something and watch SHA‑1 (legacy, insecure), SHA‑256, and SHA‑512 update live. Change one character and see the hashes flip entirely.

Interactive

Type anything — hashes update live

SHA-1 (legacy, insecure)

aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d

SHA-256

2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824

SHA-512

9b71d224bd62f3785d96d46ad3ea3d73319bfbc2890caadae2dff72519673ca72323c3d99ba5c11d7c7acc6e14b8c5da0c4663475c2e5c3adef46f73bcdec043

Change one character — the whole hash changes (avalanche effect). Same input always gives the same hash (deterministic).

So we store only the hash. On login we hash the submitted password and compare. We're safe now, right?

Attackers only have hashes—not the passwords. But they can still guess passwords, hash each guess, and see if it matches. So safety depends on how hard the password is to guess. Here's how they do it.

Cracking attacks

Brute force — Try every combination for a given length and character set. The number of possibilities is charset^length. With enough length and a rich charset, that becomes astronomically large—but short or predictable passwords are doable. Moore's law, GPUs, and cloud scaling keep raising how many guesses per second an attacker can throw.

Interactive

Brute force: how long to try every combination?

Possible combinations: 2.18e+14 · Assuming ~10¹⁰ hashes/s (GPU cluster)

Estimated time to crack: 6.1 h

Moore's law, GPU parallelism, and cloud scaling keep raising the ceiling—attackers can throw more hardware at the problem. Longer length and a richer character set make brute force impractical.

Dictionary attack — Don't try random strings first; try common passwords. "password", "123456", and so on. Lists of the top 10,000 or 100,000 most common passwords are everywhere. A permutation dictionary adds simple variants: the same words with numbers or symbols tacked on.

Interactive

Dictionary attack: common passwords first

Top ~100 common passwords — attackers try these before brute force.

123456password12345678qwerty1234567891234512341111111234567dragon123123baseballabc123footballmonkeyletmein696969shadowmaster666666qwertyuiop123321mustang1234567890michael654321superman1qaz2wsx7777777121212000000qazwsx123qwekillertrustno1jordanjenniferzxcvbnmasdfghhunterbustersoccerharleybatmanandrewtiggersunshineiloveyou2000charlierobertthomashockeyrangerdanielstarwars112233georgecomputermichellejessicapepper1111zxcvbn55555511111111131313freedom777777passmaggie159753aaaaaagingerprincessjoshuacheeseamandasummerloveashleynicolechelseabitemematthewaccessyankees987654321dallasaustinthundertaylormatrix

Permutation dictionary

A small set of words + common suffixes (e.g. 0–99) — how many combinations?

Rough permutations: 2,700 → crack time ~< 0.001 s

Rainbow tables — Precompute hashes for huge sets of passwords (e.g. every 8‑character alphanumeric string) and store them so you can look up a hash and get the plaintext. It's like carrying billions of differently shaped keys: it works if you can try them—or look them up—fast enough. The catch: you need massive storage, and (as we'll see) salting makes one table useless for many users at once.

Interactive

Rainbow tables: precomputed hash → plaintext

Precompute hashes for huge sets of passwords; store compressed "chains" so you can look up a hash and recover the plaintext. How much data for full coverage?

Combinations: 2.18e+14 → rough storage: 7943.2 TB

Like carrying billions of differently shaped keys—it works if you can try them (look them up) fast enough. Salting breaks this: every user gets a different hash for the same password, so one table can't cover everyone.

Putting it together: a leaked table of hashed passwords. Weak or common passwords get cracked quickly; stronger ones stay out of reach.

usernamepassword_hashcracked
alice5e884898da284b2b1d0e8f4c2a1b3d5e7f9a2c4b6e8d0fpassword
bob6f895899eb395c3c2e1f9g5d3b2c4e6f8a0b3d5c7f9e1123456
carola1b2c3d4e5f6789012345678901234567890abcdef
dave7g906900fc406d4d3f2g0h6e4c3d5f7g9b1c4e6d8g0f2qwerty

Even with hashing, weak or common passwords are cracked quickly. Strong or unique ones (e.g. carol) stay uncracked in practice.


Part 3: Salting — one crack shouldn't unlock everyone

Salting means adding a random (or unique) value to each password before hashing. Each user gets their own salt, stored next to their hash. On login you combine salt + password, hash that, and compare.

Why it helps: without salt, two users with the same password have the same hash. Crack one, you've cracked both. With salt, the same password produces different hashes for each user. Brute force and dictionary attacks have to target each hash separately—no reuse. Rainbow tables are built for unsalted hashes; with unique salts, one table can't cover everyone.

Interactive

Same password, two users

No salt

User 1: fcf730b6d95236ecd3c9fc2d92d7b6b2bb061514961aec041d6c7a7192f592e4

User 2: fcf730b6d95236ecd3c9fc2d92d7b6b2bb061514961aec041d6c7a7192f592e4

Same hash → attacker cracks one, gets both.

With salt

User 1: e1fa0b0fe0e4739dc536a1ceff2ec25d3ef77e3113c7da4f0ff3f4fae55708ef

User 2: 9c2d73f902dfbfd9c88599f3d6bfc8cdd061f325d0e317f2327cd36ea6aff410

Different hashes → crack each separately; rainbow tables useless.

So we store username, salt, and password_hash. Safe now, right?

Salting protects against scale: one crack doesn't unlock every user with that password, and rainbow tables don't apply. It does not protect weak passwords. "123456" and any password that's already appeared in a leak will be in attackers' wordlists and crack in a fraction of a second. Salting just means they have to do that work per user instead of once for all.

Salting protects scale — not weak passwords

Same salt or not, "123456" and any password that's been in a leak cracks in moments. Strong passwords stay out of reach.

Strong password (e.g. long passphrase)~10²⁰ years

Practically uncrackable by brute force.

Weak password (e.g. 123456)~0.02 s

Cracked almost instantly (dictionary / previous leaks).


Part 4: What actually makes a good password?

Entropy, for passwords, means unpredictability. More unpredictability means more guesses an attacker has to try. Think of it as: how many bits of "random" information are in this password? Entropy in bits is roughly log₂(possible combinations). Length and character set both matter—and so do patterns. If your "random" password is really a dictionary word or "password123", attackers will try those first.

Interactive

Enter a password or passphrase

Passwords are for humans, and humans have to remember them. A truly random password like g7$Kp9!zQ4#Lm is strong but hard to remember. A better approach is long, sentence‑based passphrases: e.g. AdamLikesHisGrapefruitsSweet. Easier to remember and, if long enough, very strong.

Length dominates complexity. A short password full of symbols and numbers can be weaker than a long phrase of simple words, because the number of combinations grows exponentially with length. Compare:

Comparison

Length dominates complexity

8-character complex vs 28-character phrase (CamelCase).

g7$Kp9!z

8 chars, mixed

~52 bits · 0.0 years

AdamLikesHisGrapefruitsSweet

28 chars, CamelCase

~160 bits · 10^31 years

The longer phrase has far higher entropy and crack time even with a smaller effective charset. Length wins.

Ways to strengthen passphrases: add punctuation, a number that means something to you, an uncommon spelling, or a structure (underscores between words, CamelCase). Each bumps effective entropy. Avoid famous quotes—they're in wordlists. Uniqueness matters more than complexity rules: reusing the same strong password everywhere is dangerous; one leak compromises all. Prefer a strong passphrase per important account, or use a password manager and unique random passwords everywhere.

Even if a service uses weak hashing (e.g. MD5), a strong long passphrase is still computationally hard to brute‑force—the guess space is simply too big.


Part 5: Modern best practices

For storing passwords, use slow, purpose‑built hashes—not raw SHA‑256. Common choices:

  • bcrypt — Widely used, configurable cost factor.
  • Argon2 — Modern winner of the Password Hashing Competition; resistant to GPU and side‑channel attacks.
  • scrypt — Memory‑hard, so scaling attacks means more RAM, not just more cores.

Slow hashing is deliberate. Fast hashes like SHA‑256 are great for checksums and signatures; for passwords you want each verification to be slow. That makes brute force expensive: an attacker doing billions of guesses per second drops to millions or thousands when each guess costs 100 ms.

Interactive

Fast hash vs slow hash (cost)

SHA-256 is designed to be fast. Password hashes like bcrypt use many iterations so each guess is slow—making brute force expensive.

Security is economics. Attackers optimize effort, target the weakest link, and scale with automation. Your job is to make the cost of breaking in higher than it's worth—strong passwords, unique per account, plus slow hashing and salting on the server.


Finale — The password philosophy

If your password leaked today, how long would it survive? Try a new passphrase in the meter below (don't use a real password).

Interactive

Enter a password or passphrase

What you can do

  • Use a password manager — Generate and store unique, strong passwords for every site (e.g. 1Password, Bitwarden, KeePass).
  • Use unique passwords — Never reuse the same password across important accounts. One leak shouldn’t unlock everything.
  • Enable 2FA — Two-factor authentication adds a second check (app or SMS). Even if a password leaks, the account is harder to take over.

Check if your email or passwords have appeared in known breaches: Have I Been Pwned