MFA (multi-factor authentication) means the user must prove who they are with factors from at least two different categories:
- something you know, like a password
- something you have, like a phone or a security key
- something you are, like a fingerprint (a biometric)
MFA stops most attacks that depend on a stolen or reused password. One example is credential stuffing: attackers try username and password pairs leaked from other sites.
The second factors differ in strength:
- SMS codes are better than nothing. But attackers can intercept them, or use SIM swapping (tricking the phone company into moving your number to their SIM card).
- TOTP apps create a code from a shared secret and the current time (RFC 6238). They work offline. But a fake login page can still steal the code and use it right away (real-time phishing).
- WebAuthn and passkeys use public-key cryptography. The key is tied to the site's origin (its exact domain), so a fake site can't use it. This makes them phishing-resistant.
On the backend, get these right:
- Encrypt TOTP secrets at rest (in the database).
- Rate limit code attempts.
- Reject codes that were already used.
- Give single-use recovery codes, and store them hashed.
- Protect MFA reset as carefully as login.
- MFA needs factors from two different categories.
- SMS is the weakest factor, because of SIM swapping and interception.
- TOTP works offline, but it can be phished.
- WebAuthn and passkeys are phishing-resistant.
- Rate limit codes, and protect secrets, recovery codes and MFA reset.
Read more