Skip to content
← pwnsy/blog
beginner22 min readMar 11, 2026

How to Set Up Two-Factor Authentication (2FA) Properly

passwords#2fa#authentication#passwords#account-security#security-awareness

Key Takeaways

  • Two-factor authentication requires two independent pieces of evidence to prove identity.
  • Time-based One-Time Passwords (RFC 6238) deserve a technical explanation because understanding them reveals both their strengths and the real-time phishing vulnerability.
  • SS7 (Signaling System 7) is the protocol telecommunications carriers use to route calls and messages internationally.
  • FIDO2 (Fast Identity Online 2nd factor) is the standard that underlies hardware security keys and passkeys.
  • Despite the real-time phishing vulnerability described above, TOTP provides strong protection against credential stuffing, remote brute force, and most casual attacks.
  • Passkeys are the successor architecture that replaces passwords and 2FA simultaneously with a phishing-resistant, seamless authentication mechanism.

In August 2022, Twilio — a cloud communications company with hundreds of millions of end users downstream — was compromised through their own employees' phone numbers. Attackers sent SMS messages to Twilio staff impersonating IT, tricked them into handing over credentials, and gained access to Twilio's internal systems. From there they accessed customer data including Authy 2FA accounts for Signal users.

In September 2023, MGM Resorts International suffered a $100 million disruption when the ALPHV/BlackCat ransomware group bypassed their authentication using a 10-minute phone call to the IT help desk, social engineering a credential reset. MGM's 2FA was apparently SMS-based for the initial access vector.

In October 2023, Okta's support system was breached partly because attackers reused credentials obtained from a prior breach against accounts without adequate MFA.

The pattern is consistent: most authentication failures in significant enterprise breaches involve either no second factor, SMS-based second factors, or push-notification MFA susceptible to fatigue attacks. The technology to prevent this has existed since 2014 — phishing-resistant hardware keys and, more recently, passkeys. Understanding why these are categorically different from SMS codes is the foundation of real authentication security.

What 2FA Actually Prevents (and What It Doesn't)

Two-factor authentication requires two independent pieces of evidence to prove identity. The "independent" requirement is critical — if both factors can be compromised by the same attack, they are not truly independent.

What properly implemented 2FA prevents:

  • Credential stuffing: attacker has your password from a breach but not the second factor
  • Brute force password attacks: the second factor blocks even a correct guess
  • Password spraying: same issue
  • Shoulder surfing: attacker sees your password typed but doesn't have the second factor
  • Data breaches exposing hashed passwords: cracking the hash gives the password but not the second factor

What SMS 2FA fails to prevent:

  • Real-time phishing proxies: attacker relays your code to the real site faster than it expires
  • SIM swapping: attacker takes over your phone number, receives your codes
  • SS7 exploitation: attacker diverts your SMS at the network layer
  • Malware on your phone: reads SMS messages

What TOTP fails to prevent:

  • Real-time phishing proxies (Evilginx, Modlishka, Muraena): these are adversary-in-the-middle proxies that relay the TOTP code in real time, completing the login before the 30-second window expires
  • Malware on the device running the authenticator app

What FIDO2/WebAuthn hardware keys and passkeys prevent that nothing else does:

  • All of the above, because the credential is cryptographically bound to the legitimate domain. When you tap a YubiKey on accounts.google.com, the cryptographic assertion is mathematically tied to that specific domain. A phishing site at accounts-google.com or even accounts.google.com.phishing.com receives an assertion that will not validate on Google's servers. Real-time relaying is impossible — you cannot forward a domain-bound signature to a different domain.

This distinction — domain binding — is why hardware keys and passkeys are categorically stronger than everything else.

The TOTP Algorithm: How Authenticator Apps Work

Time-based One-Time Passwords (RFC 6238) deserve a technical explanation because understanding them reveals both their strengths and the real-time phishing vulnerability.

When you scan a QR code to add a site to your authenticator app, you are receiving a shared secret — typically 20 bytes encoded in Base32. The QR code looks something like:

otpauth://totp/GitHub:user@example.com?secret=JBSWY3DPEHPK3PXP&issuer=GitHub&algorithm=SHA1&digits=6&period=30

The TOTP algorithm generates codes as follows:

import hmac
import hashlib
import struct
import time
import base64
 
def generate_totp(secret_b32: str, digits: int = 6, period: int = 30) -> str:
    """
    RFC 6238 TOTP implementation.
    Both the authenticator app and the server compute this identically.
    The shared secret is the only thing that ties them together.
    """
    # Decode the Base32 shared secret
    key = base64.b32decode(secret_b32.upper() + '=' * (-len(secret_b32) % 8))
 
    # T = floor(current_time / period)
    # This creates a 30-second time window where the code is valid
    timestamp = int(time.time()) // period
 
    # Encode timestamp as 8-byte big-endian unsigned int
    msg = struct.pack('>Q', timestamp)
 
    # HMAC-SHA1 of the time counter with the shared secret
    h = hmac.new(key, msg, hashlib.sha1).digest()
 
    # Dynamic truncation: use the last 4 bits of the last byte as offset
    offset = h[-1] & 0x0f
 
    # Extract 4 bytes starting at offset, clear the high bit
    code = struct.unpack('>I', h[offset:offset+4])[0] & 0x7fffffff
 
    # Reduce to desired number of digits
    return str(code % (10 ** digits)).zfill(digits)
 
# The server does the same computation and compares
# Typically accepts T-1 and T+1 windows (one period before and after)
# to account for clock drift
 
# Test
secret = "JBSWY3DPEHPK3PXP"
print(generate_totp(secret))  # Same 6-digit code your app shows right now

The critical property: no network communication is required. The code is computed entirely locally from the shared secret and the current time. This is why TOTP works in airplane mode. It also explains why TOTP is vulnerable to real-time relay — the code is valid for 30 seconds, and a phishing proxy can relay it to the real site in milliseconds.

Real-Time Phishing Proxy Attack Against TOTP

Tools like Evilginx2 automate this attack:

1. Attacker registers convincing typosquat domain: accounts-google-login.com
2. Evilginx proxies all traffic between victim and real Google
3. Victim visits phishing link, sees real Google login page (Evilginx is a transparent proxy)
4. Victim enters email and password → Evilginx captures them, relays to Google
5. Google sends TOTP challenge → Evilginx shows it to victim
6. Victim enters TOTP code → Evilginx captures it, relays to Google within milliseconds
7. Google authenticates successfully → Evilginx captures the session cookie
8. Victim is "logged in" (they see their real Google account)
9. Attacker has the session cookie, replays it from a separate browser
10. Attacker is also logged in, even after the TOTP code expires

The session cookie is the prize — it does not expire when the TOTP code does. This is documented in real incidents: the 2022 Uber breach used Evilginx against an Uber contractor, capturing their VPN credentials and TOTP code in real time.

Hardware keys and passkeys defeat this completely. The key signs a challenge that includes the origin domain. When Evilginx intercepts the authentication and forwards it to the victim, the victim's hardware key signs a challenge containing accounts-google-login.com, not accounts.google.com. The signature cannot be replayed to Google because it is bound to the wrong domain.

SMS 2FA: Why It's Broken

SS7 (Signaling System 7) is the protocol telecommunications carriers use to route calls and messages internationally. Designed in 1975 with no authentication mechanisms, SS7 allows any party with access to the signaling network to:

  • Route SMS messages intended for any phone number to a different endpoint
  • Track a phone's location globally
  • Forward calls

Access to SS7 infrastructure is not limited to telecom giants. Security researchers at SR Labs publicly demonstrated SS7 attacks against politicians in 2014. Intelligence agencies of numerous countries maintain SS7 access capabilities. Organized criminal groups with access to telecom insiders have demonstrated it in cryptocurrency theft operations.

SIM Swapping: The Practical Attack

For most attackers, SS7 access is too technical. SIM swapping is easier and more common:

  1. Attacker researches the target using social media, data broker sites, and previously breached databases
  2. Attacker calls the target's mobile carrier posing as the victim
  3. Using collected personal information (last four of SSN, billing address, recent call history), attacker convinces a customer service agent to transfer the target's number to a new SIM
  4. The target's phone immediately loses service — "No Service" or "SOS Only"
  5. Every SMS sent to the number now goes to the attacker's SIM
  6. Attacker requests password resets and SMS 2FA codes for all target accounts

Notable SIM swap cases:

  • Michael Terpin (2018): Lost $24 million in cryptocurrency after AT&T allowed a SIM swap despite a security PIN he had specifically set up to prevent unauthorized transfers. He sued AT&T for $224 million.
  • Twitter CEO Jack Dorsey (2019): His Twitter account was compromised via SIM swapping, enabling the attackers to post racist tweets from his account.
  • T-Mobile data breach (2021): 47 million customers' data was exposed, much of which is useful for conducting SIM swap social engineering.
  • Coinbase (ongoing): Coinbase has disclosed multiple incidents involving SIM swap attacks against customers, leading them to recommend TOTP over SMS for account security.

The FTC received 15,300 SIM swap complaints in 2021, a 400% increase from 2018. Actual incidents are estimated to be far higher given low reporting rates.

Warning

If your phone suddenly loses carrier service with no explanation — especially after receiving suspicious calls about your account — immediately find another device to access your accounts and change credentials. Do not wait for service to resume. By the time service returns (when the swap is reversed or detected), the attacker has already done damage.

Hardware Security Keys: The Technical Details

FIDO2 (Fast Identity Online 2nd factor) is the standard that underlies hardware security keys and passkeys. The authentication flow is fundamentally different from shared-secret approaches:

Registration

1. User visits site and initiates security key registration
2. Site sends a "challenge" — a random 32-byte value
3. Challenge includes the site's origin (relying party ID): "accounts.google.com"
4. Browser passes the challenge to the hardware key
5. Hardware key uses its on-device true random number generator to create:
   - A new ECDSA P-256 or Ed25519 key pair per-registration
   - The private key is generated inside the key and never leaves the hardware
6. Hardware key signs {challenge + origin + other data} with the private key
7. Public key + signed response sent to server
8. Server verifies the signature using the public key, stores public key for this user

Authentication

1. User visits site login
2. Server sends a new random challenge + the stored credential ID for this user
3. Browser passes to hardware key
4. Hardware key verifies the origin in the browser request matches the origin used during registration
   - If origin doesn't match → key refuses to sign → ATTACK BLOCKED
   - If origin matches → key signs {challenge + origin + counter + other data} with private key
5. Signed assertion sent to server
6. Server verifies the signature with the stored public key
7. Server verifies the counter incremented (prevents replay attacks)

The origin check in step 4 is the domain binding that defeats all phishing. The private key never leaves the hardware, so there is nothing to steal from the software layer. The counter (which increments on every authentication) prevents replay attacks even if an authentication response is captured.

CTAP2 (Client to Authenticator Protocol)

Communication between the browser and the hardware key uses CTAP2 over USB HID, NFC, or BLE:

// Simplified CTAP2 authenticatorMakeCredential request (simplified)
{
  "clientDataHash": "<sha256_of_clientDataJSON>",
  "rp": {
    "id": "accounts.google.com",
    "name": "Google"
  },
  "user": {
    "id": "<user_identifier>",
    "name": "user@example.com"
  },
  "pubKeyCredParams": [
    {"type": "public-key", "alg": -7}  // ECDSA P-256
  ],
  "options": {
    "rk": true,   // Resident key (discoverable credential)
    "uv": true    // User verification (PIN/biometric required)
  }
}

The rp.id field is the relying party identifier. The hardware key cryptographically binds the credential to this identifier. No credential created for accounts.google.com will respond to authentication challenges from any other origin.

Choosing a Hardware Key

YubiKey 5 Series — the broadest compatibility and most widely supported key. Available variants:

| Model | Connector | NFC | Best For | |---|---|---|---| | YubiKey 5 NFC | USB-A | Yes | Desktop + Android phone | | YubiKey 5C NFC | USB-C | Yes | Modern laptops + iPhone/Android | | YubiKey 5Ci | USB-C + Lightning | Yes | Mixed USB-C/Lightning environments | | YubiKey 5C Nano | USB-C | No | Always-plugged desktop use | | YubiKey Bio | USB-A or USB-C | No | Biometric PIN replacement |

Purchase directly from Yubico (yubico.com) or Amazon, not third-party gray market sellers — physical tampering before delivery is a documented threat vector for high-value targets.

Google Titan Security Key — FIDO2 compliant, Google-verified supply chain, USB-C and NFC versions. Slightly less compatible with non-FIDO2 use cases (no OTP support) but excellent for Google Workspace environments.

Nitrokey FIDO2 — European open-source hardware, independently audited, USB-A and USB-C variants. Good choice for privacy-conscious users uncomfortable with Yubico's US corporate structure.

Setting Up a YubiKey: Complete Walkthrough

Google Account:

1. myaccount.google.com → Security → 2-Step Verification
2. Scroll to "Security keys" → Add security key
3. Insert YubiKey into USB port
4. Chrome will prompt: "Register your security key"
5. Touch the gold disc on the YubiKey
6. Give the key a name: "YubiKey 5C NFC - primary"
7. Key is registered
8. Test: open new incognito window → sign into Google
   → at the 2FA prompt, insert YubiKey and touch it

GitHub:

1. github.com/settings/two_factor_authentication
2. Under "Security keys" → Register new security key
3. Enter key name, click Add → insert and touch YubiKey
4. Confirm backup methods (TOTP app or recovery codes)

AWS (IAM):

1. AWS Console → IAM → Users → your user → Security credentials
2. Multi-factor authentication → Assign MFA device
3. Select "Security key" → click Next
4. Register when prompted (insert and touch)

Cloudflare:

1. dash.cloudflare.com → Profile → Authentication → Security Keys
2. Add security key → follow browser prompt
3. Cloudflare requires a backup 2FA method — set up TOTP as backup
Tip

Always register a minimum of two hardware keys per account — one primary (daily carry) and one backup (stored in a safe or separate location). If your only key is lost or stolen, you may be locked out of accounts without fallback access. The backup does not need to be a YubiKey — a Google Titan works fine as backup to a YubiKey primary.

TOTP Apps: Choosing and Configuring

Despite the real-time phishing vulnerability described above, TOTP provides strong protection against credential stuffing, remote brute force, and most casual attacks. It is the practical minimum for all significant accounts.

App Comparison

| App | Platform | Open Source | Encrypted Backup | Cloud Sync | Notes | |---|---|---|---|---|---| | Aegis Authenticator | Android | Yes | Yes (AES-256) | Manual | Strongly recommended for Android | | Raivo OTP | iOS | Yes | Yes (iCloud) | iCloud only | Best for iPhone users | | 2FAS | iOS, Android | Yes | Yes | Optional | Cross-platform, good UI | | Ente Auth | iOS, Android, Desktop | Yes | Yes (E2E) | Yes (Ente servers/self-host) | Best cross-platform open source option | | Google Authenticator | iOS, Android | No | Yes (Google account) | Google account | Widely used; now has Google-account sync | | Authy | iOS, Android, Desktop | No | Yes (Twilio) | Twilio servers | Convenient; your TOTP secrets are on Twilio's servers | | Microsoft Authenticator | iOS, Android | No | Yes (Microsoft account) | Microsoft account | Required for some enterprise setups |

Recommended choices:

  • Android: Aegis Authenticator. Local encrypted backup, export/import, no cloud dependency, beautiful UI.
  • iOS: Raivo OTP or Ente Auth. Both are open source with encrypted backups.
  • Cross-platform: Ente Auth — open source, end-to-end encrypted sync, works on iOS/Android/desktop, can self-host the sync server.

Avoid:

  • Google Authenticator's older non-syncing versions (if you get a new phone without exporting first, you lose all accounts)
  • Authy if you're concerned about Twilio having access to your TOTP secrets (they derive your encryption key from your phone number — weaker than a proper passphrase)

Setting Up TOTP: Step-by-Step

Using GitHub as the example:

1. Install Aegis Authenticator (Android) or Raivo OTP (iOS)
   Complete initial setup — set a strong PIN or passphrase for the app

2. GitHub: Settings → Password and authentication → Two-factor authentication
   Click "Set up using an app"
   GitHub displays a QR code and your backup codes

3. In Aegis/Raivo: tap + → Scan QR code
   Point camera at the QR code
   Entry appears immediately showing a 6-digit code rotating every 30 seconds

4. Enter the current 6-digit code on GitHub to confirm setup

5. CRITICAL: Save the backup codes GitHub displayed
   Store them in your password manager under the GitHub entry
   OR print them and store physically — NOT in the same app as your TOTP

6. Test: log out of GitHub entirely → log back in
   After entering password, enter the 6-digit code from your app

Backup code storage: This step is where most people make a catastrophic mistake. If backup codes are stored only on the same phone as the authenticator app, losing the phone means losing the backup. Store backup codes in your password manager, in a separate notes app with its own encryption, or printed and physically secured.

TOTP Backup: The Critical Configuration People Skip

Aegis (Android): Tools → Backup → Enable automatic backup → choose an encrypted local backup location. Also export manually: Tools → Export → Encrypted → save to a location you control (not just the phone).

# Aegis backup is an encrypted JSON file
# Test restoring it before you need to:
# - Install fresh Aegis → Import → select backup file → enter backup password → all accounts restored

Raivo (iOS): iCloud sync is built-in. Verify it's enabled: Settings → iCloud → on. Your TOTP accounts are backed up to iCloud with app-level encryption.

Ente Auth: Cloud sync is automatic and end-to-end encrypted. Export for offline backup: Settings → Data → Export.

Passkeys: The Authentication Evolution

Passkeys are the successor architecture that replaces passwords and 2FA simultaneously with a phishing-resistant, seamless authentication mechanism.

What a Passkey Is

A passkey is a FIDO2 discoverable credential (resident key) stored by your device's secure enclave or by a password manager. Instead of a 6-digit code, authentication requires:

  1. Server sends a challenge to your browser/app
  2. Browser asks the operating system to authenticate
  3. OS triggers biometric prompt (Face ID, Touch ID, Windows Hello, device PIN)
  4. If approved, the device's secure enclave signs the challenge with a private key that never leaves the secure hardware
  5. Signed assertion returned to server
  6. Server verifies with stored public key

No password is transmitted. No code is transmitted. The biometric unlock is local — it is not sent to the server; it unlocks the private key on-device.

Platform Passkey Support

Apple (iOS 16+, macOS 13+): Passkeys sync via iCloud Keychain, encrypted end-to-end. Available across all Apple devices associated with the same Apple ID.

# Creating a passkey on GitHub (Safari on macOS):
1. github.com/settings/passkeys → Add passkey
2. Safari shows: "Do you want to save a passkey for github.com?"
3. Authenticate with Touch ID / Face ID
4. Passkey created and synced to iCloud Keychain

# Next login:
1. Click "Sign in with passkey" or GitHub detects stored passkey
2. Touch ID / Face ID prompt appears
3. Authenticate → logged in, no password, no code

Android (9+): Passkeys sync via Google Password Manager. Available across Android devices signed into the same Google account.

Windows (10/11): Windows Hello supports passkeys. Stored locally or synced via Microsoft account.

Cross-platform managers: 1Password, Bitwarden (experimental), Dashlane, and Strongbox (iOS) can store passkeys independently of OS platform sync. This enables passkeys that work across Apple, Windows, and Android without being tied to a single vendor's sync infrastructure.

Passkey Technical Format

// Passkey WebAuthn credential (what's stored server-side — public info only)
{
  "id": "dGhpcyBpcyBhIHRlc3Q",          // Credential ID (random bytes)
  "type": "public-key",
  "rawId": "<base64url_credential_id>",
  "response": {
    "clientDataJSON": "<base64url>",      // Contains origin, challenge, type
    "attestationObject": "<base64url>"   // Contains public key, attested info
  }
}
 
// The server stores:
// - Credential ID (to look up the credential)
// - Public key (to verify future assertions)
// - Sign count (to detect cloned authenticators)
// - Relying party ID: "github.com"
//
// The server NEVER receives or stores:
// - Private key (stays in device secure enclave forever)
// - Biometric data

Implementing Passkeys on Your Application

If you are building authentication, adding passkey support is worth the implementation cost:

// Frontend: Register a passkey (WebAuthn API)
async function registerPasskey(username) {
  // 1. Get challenge from your server
  const response = await fetch('/auth/passkey/begin-registration', {
    method: 'POST',
    body: JSON.stringify({ username }),
    headers: { 'Content-Type': 'application/json' }
  });
  const options = await response.json();
 
  // 2. Create credential
  const credential = await navigator.credentials.create({
    publicKey: {
      challenge: base64urlDecode(options.challenge),
      rp: {
        name: "Your App",
        id: "yourapp.com"
      },
      user: {
        id: base64urlDecode(options.userId),
        name: username,
        displayName: username
      },
      pubKeyCredParams: [
        { type: "public-key", alg: -7 },   // ES256
        { type: "public-key", alg: -257 }  // RS256 (legacy)
      ],
      authenticatorSelection: {
        residentKey: "required",      // Makes it discoverable (passkey)
        userVerification: "required"  // Require biometric/PIN
      },
      timeout: 60000,
      attestation: "none"  // Direct/indirect/none — none is fine for most apps
    }
  });
 
  // 3. Send credential to server for storage
  await fetch('/auth/passkey/complete-registration', {
    method: 'POST',
    body: JSON.stringify({
      id: credential.id,
      rawId: base64urlEncode(credential.rawId),
      response: {
        clientDataJSON: base64urlEncode(credential.response.clientDataJSON),
        attestationObject: base64urlEncode(credential.response.attestationObject)
      },
      type: credential.type
    }),
    headers: { 'Content-Type': 'application/json' }
  });
}
# Backend: Verify passkey registration (using py_webauthn)
from webauthn import verify_registration_response, generate_registration_options
from webauthn.helpers.structs import RegistrationCredential
 
def complete_passkey_registration(user_id: str, credential_data: dict):
    try:
        verification = verify_registration_response(
            credential=RegistrationCredential.parse_raw(json.dumps(credential_data)),
            expected_challenge=get_pending_challenge(user_id),  # From session/cache
            expected_rp_id="yourapp.com",
            expected_origin="https://yourapp.com",
        )
 
        # Store credential in database
        store_passkey(
            user_id=user_id,
            credential_id=verification.credential_id,
            public_key=verification.credential_public_key,
            sign_count=verification.sign_count,
        )
 
        return True
    except Exception as e:
        log.error(f"Passkey registration failed: {e}")
        return False

MFA Fatigue Attacks: Push Notification Vulnerabilities

MFA push notifications ("approve or deny" prompts from apps like Duo, Okta Verify, or Microsoft Authenticator) are vulnerable to "prompt bombing" or MFA fatigue attacks. The technique:

  1. Attacker obtains the victim's credentials (phishing, purchase, breach)
  2. Attacker logs in repeatedly at odd hours, generating continuous push notifications
  3. Eventually the victim taps "Approve" to make the notifications stop, incorrectly assuming it is a system error
  4. Attacker gains access

Real incident: The September 2022 Uber breach. The attacker purchased credentials for an Uber contractor on the dark web, then conducted a WhatsApp social engineering campaign alongside prompt bombing. The contractor eventually approved an MFA push notification after receiving a WhatsApp message claiming to be Uber IT Support explaining the notifications.

The Lapsus$ group systematically used MFA fatigue against Microsoft, Okta, Cisco, Samsung, and Nvidia in 2022. Their methodology was consistent: buy or social engineer credentials, prompt bomb push MFA, follow up with a social engineering phone call to "explain" the prompts.

Defenses against MFA fatigue:

  1. Number matching: The authenticator app displays a number shown on the login screen. The user must enter that number in the app to approve. An automated prompt bomb has no number to provide.
  2. Additional context: Push notifications show the IP address and geographic location of the login attempt. Users can see "Login request from Lagos, Nigeria" and deny correctly.
  3. Rate limiting push prompts: Limit to 3 push approvals per time window; after that, require the user to authenticate with a different method.
  4. Use phishing-resistant MFA for privileged accounts: Hardware keys cannot be fatigue-attacked.

Microsoft Entra ID (formerly Azure AD) and Okta both support number matching — enable it.

Which Accounts Absolutely Require MFA

Not all accounts carry equal risk. Prioritize by what damage a compromise enables.

Tier 1 — Protect first, no exceptions:

| Account Type | Why It's Critical | |---|---| | Primary email | All password resets flow here; owning email = owning everything | | Password manager | Compromise exposes every stored credential | | Domain registrar | Losing a domain redirects email and takes down infrastructure | | Cloud DNS provider | Same as domain registrar impact | | Primary cloud provider (AWS/Azure/GCP) | Contains infrastructure, data, billing |

Tier 2 — Protect within 24 hours:

| Account Type | Why It Matters | |---|---| | Banking and financial accounts | Direct monetary loss | | Cryptocurrency exchanges | Irreversible monetary loss | | Cloud storage (Drive, iCloud, Dropbox) | Contains documents, credentials, 2FA backup codes | | Social media with significant reach | Reputational and identity fraud | | Work accounts (corporate email, Slack, GitHub) | Professional consequences, data exposure |

Tier 3 — Protect when convenient:

Everything else. Any account that stores personal information, payment details, or has a password reset path through a Tier 1 or Tier 2 account.

Recovery Planning: When 2FA Fails You

Setup is incomplete until you have handled recovery. The failure modes are predictable: lost phone, broken hardware key, corrupted authenticator app data. If you have not thought through recovery before the failure occurs, you will either lose account access permanently or waste hours on hold with support teams.

Backup Code Management

Every TOTP-based service generates 8–10 single-use backup codes during setup. These are your escape hatch when your authenticator is unavailable.

# Example backup codes (do not use these):
1a2b-3c4d
5e6f-7g8h
9i0j-1k2l
3m4n-5o6p
7q8r-9s0t

Storage rules:

  • Store backup codes in your password manager, under the entry for the site they belong to
  • Print a physical copy for the 5 most critical accounts (email, banking, password manager, primary cloud, primary domain registrar)
  • Never store backup codes in the same authenticator app the codes are meant to recover
  • Never screenshot backup codes to the photo library on the same phone

Multiple Enrolled Devices

For hardware keys: buy two of the same model. Register both on every critical account. Primary key stays on your keychain; backup stays in a drawer at home or at the office.

For TOTP: if you scan the QR code into two authenticator apps simultaneously (both apps scan the same QR code during setup), both generate identical codes. This provides redundancy — the desktop and the phone have the same TOTP seed. Do this during initial setup only; once setup is complete, you cannot recover the seed without going through the account's 2FA settings again.

Testing Your Recovery Before You Need It

# Checklist for testing backup access:
 
# TOTP:
 Export encrypted backup from Aegis/Raivo/Ente
 Install app fresh on a second device OR wipe and reinstall
 Import backup verify all accounts load correctly
 Generate and verify a code for one account
 
# Hardware key:
 Log into a test account using the backup key only
 Verify it works before storing it away
 
# Backup codes:
 Use one backup code to log in (you have 8-10, using one to verify they work is acceptable)
 Generate new codes if you use one (most services let you regenerate)

Common 2FA Mistakes That Get People Owned

Single point of failure on one device: Your phone is your only authenticator. It is stolen. The attacker has your PIN (observed or guessed). They now have both your passwords (via browser-saved passwords or a cloud-syncing password manager unlocked by biometrics) and your TOTP codes. Solution: keep one hardware key registered per critical account as an independent factor.

Using email as 2FA recovery before securing email: Setting up TOTP on your bank, with the recovery path routing through an unsecured Gmail account, provides minimal protection. An attacker who owns your email can bypass your bank's 2FA through account recovery. Secure email with MFA first — it is the root of the recovery chain.

Trusting "remember this device" indefinitely: Every trusted device is a window where 2FA is not checked. Review and prune trusted devices regularly. Most services show this under active sessions or trusted devices in security settings.

SMS as 2FA on cryptocurrency accounts: Crypto exchanges are primary SIM swap targets because the losses are immediate and irreversible. Coinbase, Binance, Kraken, and all major exchanges support hardware key authentication. Use it.

Push notification MFA without number matching on privileged accounts: As described in the Uber breach analysis above — enable number matching in your organization's identity provider settings. This single configuration change defeats prompt bombing attacks entirely.

Authenticator app without encrypted backup: New phone, old authenticator data not migrated. Result: locked out of 15 accounts, spending days on hold with support teams to disable 2FA. Configure encrypted cloud backup in your authenticator app the day you set it up.

Action Checklist

Today:

  • [ ] Install Aegis (Android), Raivo or Ente Auth (iOS), or Ente Auth (cross-platform)
  • [ ] Enable TOTP on your primary email account — save backup codes
  • [ ] Enable TOTP on your password manager account — save backup codes
  • [ ] Enable TOTP on your primary bank account (if TOTP is available; hardware key if possible)

This week:

  • [ ] Enable TOTP on all Tier 1 and Tier 2 accounts listed above
  • [ ] Export and test a backup from your authenticator app
  • [ ] Store backup codes for all critical accounts in your password manager
  • [ ] Print backup codes for email, banking, and password manager — store physically

Next month:

  • [ ] Purchase two hardware security keys (YubiKey 5C NFC recommended for most people)
  • [ ] Register both keys on: email, password manager, primary cloud provider, GitHub/GitLab
  • [ ] Enroll passkeys on every service that supports them
  • [ ] Audit trusted devices on all major accounts — remove devices you no longer own
  • [ ] If you manage identity for an organization: enable number matching for push MFA, enforce FIDO2 for privileged accounts via Conditional Access or Okta Policies

The hierarchy is clear: passkeys and hardware keys are phishing-proof; TOTP is phishing-vulnerable but stops everything else; SMS is weak but better than nothing; no 2FA is negligence. Move up the hierarchy starting today with the accounts that matter most.

Sharetwitterlinkedin

Related Posts