open atlas
↑ Back to track
Security Foundations SECF · 03 · 01

From passwords to MFA

Passwords fail at scale: people reuse them, dumps fuel credential stuffing, and storage mistakes turn one breach into millions of cracked accounts. MFA helps but is routinely phished; only phishing-resistant factors like passkeys actually hold.

SECF Middle ◷ 15 min
Level
FoundationsJuniorMiddleSenior

A breach hits a forum nobody on your team has ever heard of. Two weeks later your own login endpoint is taking 40,000 requests an hour, each one a real email-and-password pair, each one returning 200 OK for about 1 in 200 attempts. You weren’t breached. Your users were — somewhere else, years ago — and they reused the same password on you. The attacker didn’t crack anything. They replayed a list. This is credential stuffing, and it is the single most common way accounts get taken over today, precisely because it routes around every defense that assumes the attacker has to break your system.

By the end of this lesson you’ll know why passwords fail at scale, exactly how MFA gets bypassed in the field, and which factors are actually phishing-resistant.

Why passwords fail at scale

A password is a shared secret: the user knows it and your server knows it. That symmetry is the whole problem. The user reuses the secret across dozens of sites, so its safety is only as good as the worst site they ever typed it into — and you don’t control that site. Studies of large breach corpora put password reuse north of 50% of users; that single fact is why a dump from an unrelated service becomes an attack on yours.

Three failure modes compound:

  • Reuse → credential stuffing. Attackers take billions of leaked email:password pairs (the “Collection #1” dump alone held ~773M unique emails) and replay them against your login with rotating proxies. Success rates of 0.1–2% sound tiny until you multiply by millions of attempts: a 0.5% hit rate on 10M tries is 50,000 compromised accounts, and your logs just show a lot of valid logins.
  • Guessing → brute force and spraying. Against a single account, rate limits and lockouts help. Password spraying sidesteps them: try one common password (Winter2024!) against ten thousand accounts, so no single account trips a lockout. Per-account counters never fire; you need per-source and per-credential-pattern detection.
  • Storage mistakes → offline cracking. If your password store leaks, the attacker’s clock starts. Plaintext or unsalted MD5/SHA-1 means the whole table is cracked in hours on a commodity GPU rig doing tens of billions of guesses per second. A correctly chosen slow, salted, memory-hard hash (Argon2id, scrypt, or bcrypt) is what buys you time — salt kills precomputed rainbow tables and forces per-account work, and the deliberate slowness caps the attacker’s guess rate.

The senior reflex: treat your password store as already leaked and design the hash so that even then, cracking is economically painful. And treat every successful login as possibly an attacker with a valid password — which is exactly the gap a second factor is meant to close.

How MFA helps — and how it’s bypassed

Multi-factor authentication requires evidence from more than one category: something you know (password), something you have (a phone, a security key), something you are (a fingerprint). The point isn’t that any one factor is strong; it’s that an attacker now needs to compromise two unrelated things at once. Against bulk credential stuffing this is devastating: the bot has the password but not the second factor, so the automated takeover dies. Microsoft and Google have both reported MFA blocking well over 99% of automated account-compromise attempts. That number is real — and it’s also where people stop thinking, which is the dangerous part.

MFA is not binary. The factors form a strength ladder, and a targeted attacker (as opposed to a bulk bot) defeats the weak rungs routinely:

  • SMS one-time codes are phishable and interceptable. SIM-swap fraud — socially engineering a carrier to port the victim’s number to the attacker’s SIM — delivers the code straight to the attacker. SMS is better than nothing and far better than nothing against bots, but NIST has discouraged it as a primary factor for years.
  • TOTP authenticator apps (the 6-digit code that rotates every 30 seconds, RFC 6238) remove the carrier from the loop — the secret lives on the device. But the code is still something the user can read and type, which means the user can be tricked into typing it into a fake site.
  • Push notifications add a tap, but introduce MFA fatigue: spam the victim with prompts at 3am until they tap “approve” to make it stop. This is how Uber and others were breached. Number-matching (the prompt shows a number the user must enter) blunts it, but doesn’t eliminate the human in the loop.

The bypass that ties the weak rungs together is the real-time phishing proxy (Evilginx, Modlisha and friends). The victim lands on a pixel-perfect proxy of your login. They enter their password — the proxy relays it to the real site. The real site asks for the TOTP code — the proxy relays the prompt, the victim types the code, the proxy relays that too. The real site issues a session cookie, and the proxy steals it. The attacker now has an authenticated session and never needed the password or the code again. Every factor the user types is replayable through this man-in-the-middle, which is the whole reason “phishing-resistant” is a distinct, harder property.

Why this works

Why doesn’t a one-time code stop a phishing proxy if it’s only valid for 30 seconds? Because the proxy is live. It isn’t saving the code for later — it’s relaying it the instant the victim types it, inside the same 30-second window, to the genuine site. The OTP does exactly its job (proves a fresh code) and still loses, because nothing in the protocol binds the code to which site the user is actually talking to. That missing bind — between the credential and the origin — is the precise gap phishing-resistant factors close.

Phishing-resistant factors

A factor is phishing-resistant when stealing what the user typed gives the attacker nothing — because there’s nothing typeable to steal. The mechanism is origin-bound public-key cryptography, standardized as WebAuthn / FIDO2 and shipped to users as passkeys and hardware security keys.

Here’s the mechanism that matters. At registration, the authenticator (a YubiKey, or the secure element in a phone) generates a new key pair scoped to your exact origin, https://yourbank.com. The private key never leaves the device; the server stores only the public key. At login, the server sends a random challenge; the authenticator signs it with the private key and the browser includes the real origin in what gets signed. Two consequences fall out of that single design choice:

  1. There is no shared secret to phish. The user never types anything an attacker could replay. A signature for one challenge is useless for the next.
  2. The origin binding kills the proxy. If the victim is on yourbank.evil.com, the browser signs that origin — and your server, expecting yourbank.com, rejects the signature. The Evilginx relay collapses, because the credential refuses to work anywhere but the genuine site. This is the property SMS, TOTP, and push all lack.
FactorCategoryStops bulk stuffing?Stops real-time phishing?Defeated by
Password onlyKnowNoNoReuse, dumps, cracking
SMS OTPHave (weak)YesNoSIM swap, proxy relay
TOTP appHaveYesNoReal-time proxy (typed code)
Push + number-matchHaveYesPartlyMFA fatigue, relay
Passkey / security keyHave + areYesYesDevice theft + local unlock

The senior takeaway is a threat-tiered one. Any MFA crushes the bulk-bot problem, so the cheapest big win is enabling some second factor for everyone. But for accounts that matter — admins, finance, anyone with production access — typed factors are not enough, because those are exactly the accounts a human attacker will phish in real time. There, phishing-resistant WebAuthn is the bar. And don’t let MFA become a single point of failure: weak account-recovery (“lost my phone, text my old number”) is the back door that quietly downgrades your strongest factor to your weakest one.

Pick the best fit

Your admins keep getting phished: attackers stand up a fake login proxy, relay the password and the TOTP code in real time, and steal the session cookie. TOTP MFA is already enabled. Pick the fix that actually closes this.

Quiz

Your login is getting millions of attempts using real email/password pairs, with a ~0.5% success rate and no single account locking out. What is this, and what's the root enabler?

Quiz

Why does a passkey (WebAuthn) defeat a real-time phishing proxy when a TOTP code does not?

Order the steps

Order these auth factors from weakest to strongest against a targeted, human-driven attacker:

  1. 1 Password only (reused, phishable, crackable)
  2. 2 SMS OTP (phishable + SIM-swappable)
  3. 3 TOTP app (phishable: code is typed)
  4. 4 Push + number-matching (resists fatigue, still relayable)
  5. 5 Passkey / security key (origin-bound, phishing-resistant)
Recall before you leave
  1. 01
    Explain why passwords fail at scale and name the three compounding failure modes.
  2. 02
    What makes a factor phishing-resistant, and why do SMS, TOTP, and push all fail that bar while passkeys pass it?
Recap

Passwords fail at scale because they’re shared secrets users reuse: a breach at any site becomes credential stuffing against yours, where billions of leaked pairs are replayed and even a fraction-of-a-percent hit rate produces mass account takeover. Spraying dodges per-account lockouts, and a leaked store gets cracked fast unless it’s hashed with a slow, salted, memory-hard function (Argon2id, scrypt, bcrypt). MFA is the answer to stuffing — it blocks over 99% of automated takeovers — but it isn’t binary. SMS is phishable and SIM-swappable, TOTP and push are relayed by real-time phishing proxies that forward the typed code and steal the session cookie, and push adds MFA-fatigue spam. The factor that actually holds against a targeted human is phishing-resistant WebAuthn: a passkey or security key signs an origin-bound challenge with a private key that never leaves the device, so a signature made on a fake domain is rejected and there’s no secret to relay. So the senior posture is tiered — enable any MFA for everyone to kill the bots, require passkeys for the accounts that matter, hash as if the store is already leaked, and watch that account recovery doesn’t become the back door around all of it.

Practice

Start at the top. Tasks go easiest → hardest: recall a fact, apply it to a case, then a senior-level stretch. Open one, attempt it, then reveal.

recallapplystretch0 of 6 done
Connected lessons

Something unclear?

Ask a question about this lesson. Questions are anonymous and go straight to the author to make the lesson better.

shortcuts expand
search
K
prev piece
k
next piece
j
cycle tier
t
this menu
?
sources2
expand
  1. 01
  2. 02

Trademarks belong to their respective owners. Editorial reference only.