Lesson 16 / 25

MFA Options

TOTP, push, SMS and their weaknesses.

Not all second factors are equal

TOTP (RFC 6238) derives a short code from a shared secret and the current time step (commonly 30 seconds, 6 digits) in an authenticator app; it works offline but can be phished in real time. Push approval is convenient but invites MFA fatigue: attackers spam prompts until a user taps approve; number matching (typing a number shown on the login screen) reduces this. SMS and voice codes are the weakest common option: vulnerable to SIM swapping, number porting and interception, and NIST SP 800-63B treats SMS as a restricted authenticator. Security keys and passkeys (WebAuthn) are phishing-resistant. Offer recovery codes generated once, stored hashed and single use, and treat MFA enrolment and removal as sensitive actions requiring re-authentication.

Beyond the password

Extra factors and public-key credentials stop most account takeover; federated login brings its own pitfalls.

Three ideas: MFA options, passkeys and WebAuthn, social login and account linking.
Figure 6.1 — Factor, passkey, federation.

TOTP enrolment and verification

Using pyotp; store the secret encrypted, and prevent code reuse within the window.

import pyotp

def start_enrolment(user):
    secret = pyotp.random_base32()
    db.mfa.save_pending(user.id, encrypt(secret))
    uri = pyotp.TOTP(secret).provisioning_uri(name=user.email, issuer_name='ExampleApp')
    return uri  # render as a QR code for the authenticator app

def verify_code(user, code: str) -> bool:
    secret = decrypt(db.mfa.get_secret(user.id))
    totp = pyotp.TOTP(secret)
    if not totp.verify(code, valid_window=1):     # allow +/- one 30s step of drift
        return False
    if db.mfa.code_used_recently(user.id, code):  # block replay inside the window
        return False
    db.mfa.mark_used(user.id, code)
    return True

Rate limit the second factor too

A 6-digit code has only a million possibilities. Without attempt limits on the MFA step, attackers who already hold the password can brute-force it.

Quick check: Why is SMS considered a weak second factor?

  • SMS cannot be rate limited
  • SMS codes are always longer than TOTP codes
  • Codes can be stolen through SIM swapping or interception
  • SMS requires the user to know a password
Answer

Codes can be stolen through SIM swapping or interception — Phone numbers can be hijacked without touching the user's device.