Multi-Factor Authentication
Requiring a second, independent kind of evidence — something you have or something you are, alongside something you know — with the honest ranking of which factors resist phishing and which merely resist password reuse.
Frame the problem
Security starts with a concrete asset, attacker capability and trust crossing.
Three factor types, and why independence matters
Knowledge — something you know: a password, a PIN, an answer to a question. Copyable, phishable, reusable, and frequently already public.
Possession — something you have: a phone running an authenticator app, a hardware security key, a registered device with a stored private key. Requires the attacker to be in physical or logical control of a thing.
Inherence — something you are: a fingerprint or a face. In practice on modern devices this is not a factor sent to your server at all; it unlocks a private key held on the device, which means the factor your server sees is possession. That distinction matters when reasoning about what a compromise actually gives an attacker.
Two factors of the *same* type are not multi-factor. A password plus a security question is two pieces of knowledge, both copyable in the same breach, both phishable in the same form. The security benefit comes from the factors failing independently, so an attacker must run two different attacks with two different capabilities.
| Factor | Resists password reuse | Resists phishing | Main weakness |
|---|---|---|---|
| SMS one-time code | Yes | No | SIM swap, network interception, and fully phishable — the code is just relayed |
| Email one-time code | Partly | No | Email is often the recovery root; compromising it defeats both factors at once |
| TOTP authenticator app | Yes | No | Phishable in real time: a proxy site collects the code and uses it within its window |
| Push approval | Yes | Weakly | Notification fatigue — repeated prompts until the user taps approve |
| Push with number matching | Yes | Partly | Better, but still relies on the user reading and comparing correctly |
| Passkey / security key (WebAuthn) | Yes | Yes — by design | Enrolment, recovery and device loss are the hard parts |
Phishing resistance is a structural property
The reason TOTP and SMS are phishable is not implementation quality; it is that the user is asked to *transcribe a secret into a form*. A convincing fake login page collects the password and the code and relays both to the real site immediately. The code is valid for the attacker because nothing in the protocol ties it to the site the user believed they were on.
WebAuthn changes the structure. The authenticator signs a challenge together with the origin of the site requesting it, and the browser supplies that origin — the user cannot type it, mistype it, or be persuaded to substitute it. A signature produced for evil-app.example is simply not valid at app.example, so a relayed credential is useless. That is what "phishing-resistant" means: not that users are harder to fool, but that fooling them no longer produces anything the attacker can use. See Passkeys and WebAuthn.
This is why "we have MFA" is an incomplete statement in a security review. The follow-up question — *which factor, and is it phishing-resistant?* — separates a control that stops the dominant real attack from one that stops password reuse only. Both are worth having; they are not the same control.
The parts that get forgotten
Recovery codes are single-use secrets shown once at enrolment. They are a knowledge factor with the power of a possession factor, so they must be hashed at rest exactly like passwords, marked used atomically, and regenerable as a set. A recovery code stored in plaintext is an MFA bypass sitting in your database.
Recovery and support are where MFA is most often defeated without any technical attack. If a password reset does not require the second factor, MFA protects the login form and nothing else. If a support agent can disable MFA on a convincing phone call, MFA protects against remote attackers and not against social engineering — which is the branch a determined attacker will take, as Attack Trees makes visible.
Step-up decides how much a stolen session is worth. Even with MFA at login, a hijacked session can typically change the email address, add an API token, or disable MFA. Requiring the factor again for those specific operations is inexpensive and closes the most valuable post-compromise actions.
Enrolment is a boundary too: the moment a second factor is registered, whoever registered it controls the account going forward. Enrolment should require the current credential, notify the user, and — for high-value accounts — be blocked from a session that is itself new or unusual.
- Require the second factor on password reset, or the reset flow is the bypass.
- Hash recovery codes at rest, mark them used atomically, and rotate the whole set when one is used.
- Require step-up for: change email, change password, disable MFA, create API token, add OAuth app, change payout details.
- Notify on every MFA enrolment, removal and recovery-code use, through a channel the attacker did not just change.
- Have a defined, verified process for support-assisted recovery; it will otherwise become the weakest branch.
Key points
- Two factors must be of different *types*; a password plus a security question is one factor twice.
- SMS and TOTP resist password reuse but not phishing — a real-time proxy relays the code inside its window.
- WebAuthn is phishing-resistant structurally, because the signature is bound to the origin the browser supplies.
- Recovery codes are password-equivalent secrets and must be hashed, single-use and revocable as a set.
- MFA at login without MFA on reset, on support recovery and on step-up protects the login form only.
Boundary control exercise
This lesson uses the shared boundary-control exercise.
Follow the attack
Safe conceptual simulation: capability → missing control → crossed boundary → asset impact.
- 1Attacker → obtain the password: reuse, phishing, or a breach corpus.
- 2Password → the second factor: relay a TOTP code through a proxy page, intercept an SMS via SIM swap, or fatigue the user with push prompts.
- 3Blocked → change branch: attack account recovery or the support process, where the factor is often not required.
- 4Access → persistence: add a second factor of the attacker's own, so the real user's device no longer matters.
- Full account takeover despite MFA, if the factor is phishable or the recovery path skips it.
- Attacker-enrolled factors lock out the legitimate owner and survive a password reset.
- For administrative accounts, this is the top branch of nearly every attack tree.
Defend, detect, recover
One prevention is a single point of security failure. Layer it and make failure observable.
- • Prefer phishing-resistant factors (passkeys, security keys); offer TOTP as a fallback and treat SMS as a last resort.
- • Require the factor on reset, recovery and step-up — not only at login.
- • Hash recovery codes; make enrolment require the current credential and always notify.
- • Define and rehearse the support-assisted recovery process, with identity verification proportionate to account value.
- • Alert on MFA disablement, on new factor enrolment, and on recovery-code use.
- • Alert on repeated push prompts to one user in a short period — the signature of fatigue attacks.
- • Alert on successful MFA from a device and location never seen for that account.
- • Remove attacker-enrolled factors, revoke all sessions and tokens, and re-enrol the legitimate user through a verified channel.
- • Regenerate the entire recovery-code set.
- • Review what was done during the window, especially the persistence primitives.
- • Device loss and recovery remain the hard problem; every recovery path is a potential bypass.
- • Phishing-resistant factors require enrolment, and the enrolment period is a window of weaker protection.
- • Malware on an authenticated device operates inside the session and is unaffected by any factor.