Skip to content

Chapter 6 of 129 min readBy Deepak GuptaPublished

MFA Without Fatigue

In September 2022, an attacker bought an Uber contractor's corporate password, then kept trying to log in. The contractor received approval request after approval request. The first ones blocked access. Then, in Uber's words, "the contractor accepted one, and the attacker successfully logged in." (Uber)

The MFA worked exactly as configured. The human on the other end did not, because the system had taught them that approval prompts are routine.

Repeated MFA prompts are not extra security. They train users to approve requests without reading them, and attackers exploit exactly that habit. This chapter shows how to ask less often and mean it more when you do.

What push bombing is

CISA calls it MFA fatigue or push bombing: an attacker with a stolen password floods the user with approval requests until one gets tapped by accident or out of annoyance. (CISA number matching fact sheet) A brand that prompts constantly has already done the attacker's conditioning for them.

The same CISA fact sheet notes that attackers can generate hundreds of prompts in a short period, and that the stolen password usually comes from password spraying or a dump from another breached site. Its preferred fix is phishing-resistant MFA. Number matching, where the user types a number shown on the login screen into the app, is the interim fix for teams that cannot move yet.

The incident pattern

Push bombing follows a recognizable shape. Knowing it lets you detect the attack at step two instead of reading about it at step five.

  1. The password is already gone. It was reused from a breached site, sprayed, or phished. Nothing in your logs looks unusual yet.
  2. A burst of prompts. Several approval requests arrive close together, often outside the user's normal hours, from a device and network the account has never used.
  3. Denials, then pressure. The user denies a few. Some attackers then contact the user directly, posing as support, to "explain" the prompts.
  4. One approval. The user taps approve to make it stop, or because one request looked legitimate.
  5. Persistence. The attacker registers a new device or factor, or changes the contact email, so the real owner cannot take the account back.

CISA's guidance gives you the most useful detection rule in the whole pattern. Investigate denied push notifications, because MFA runs after the password check, so a denial can mean the password is compromised (CISA). A denied prompt is not noise. It is a free alert.

A response playbook for the pattern

Signal Automatic response Human follow-up
One denied prompt Log it, keep the account open None
Two or more denied prompts in a short window Stop sending prompts for that account; require a passkey or a verified recovery path Notify the owner on every known channel
Prompts from a new device in a new country Hold the sign-in for step-up with a phishing-resistant factor Review if the account has payout or admin rights
An approval followed by a new factor or email change Block the change behind a cool-down; alert the owner on the old contact point Treat as a likely takeover and start recovery

Tune the windows to your own data. The shape matters more than the exact numbers.

Ask once, remember the device, step up at sensitive moments

The UK NCSC describes the model directly. Complete MFA on a new device, let the user mark it trusted, and do not ask again until that device stops looking trustworthy. Require MFA again for high-risk actions such as changing important settings or approving large transactions. (NCSC)

Microsoft flags tenants that remember MFA on trusted devices for fewer than 30 days and recommends 90. (Microsoft Learn)

On session length, NIST Rev 4 says the overall reauthentication timeout should be no more than 24 hours at AAL2, and the inactivity timeout should be no more than 1 hour. (NIST SP 800-63B-4) Both are "should" recommendations, while setting a definite timeout is a "shall".

For most consumer accounts, that translates into a long-lived session on a trusted device, with fresh proof required only when the user does something that matters. With a passkey, that fresh proof is a two-second face or fingerprint check, not a code. That is the difference between step-up that users resent and step-up they barely notice.

Rank factors by phishing resistance

Factor Phishing-resistant Main weakness Where to use it
Passkey or FIDO security key Yes Recovery when all devices are lost Default for sign-in and step-up
Authenticator app code (TOTP) No Relayed by phishing pages Fallback for users without passkey support
Push with number matching No Reduces blind approvals, still phishable Interim control while migrating
Push without number matching No Push bombing Retire it
SMS or voice code No SIM swap, SS7 interception, phishing Last-resort fallback only

Classifications per CISA via SecurityWeek. NIST Rev 4 also requires services at AAL2 to offer at least one phishing-resistant option (NIST SP 800-63B-4, MojoAuth on NIST 800-63B).

Phishing resistance is not the whole story, though. Even a passkey can be undermined if an attacker steals a session after sign-in. I cover that class of attack in Pass-the-Passkey Bypasses MFA Without Breaking FIDO2. The defense is the same as this chapter's theme: bind sensitive actions to fresh proof, not to an old session.

A sample step-up policy

Step-up means asking for fresh proof at a named moment, not on a timer. Here is a starting policy you can adapt. Write yours in this shape, in a file your engineers, security team, and product managers all read.

# Step-up policy v1 (adapt to your product and fraud data)
session:
  trusted_device_remember_days: 90
  max_session_age_hours: 24          # NIST Rev 4 AAL2 recommendation
  inactivity_timeout_hours: 1        # tighten only for sensitive views

factors:
  phishing_resistant: [passkey, security_key]
  fallback: [email_code, totp]
  last_resort: [sms_code]

step_up:
  - action: view_sensitive_data      # statements, health, tax documents
    require: any_factor
    if_session_older_than_minutes: 30
  - action: change_email_or_phone
    require: phishing_resistant
    notify: [old_contact, all_devices]
    cool_down_hours: 24              # before the old contact stops working
  - action: add_payout_or_payee
    require: phishing_resistant
    notify: [all_devices]
    hold_first_payment_hours: 24
  - action: add_passkey_or_factor
    require: existing_factor
    notify: [all_devices]
  - action: account_recovery
    require: verified_recovery_factor
    cool_down_hours: 24
    support_can_bypass: false

rate_limits:
  prompts_per_account_per_hour: 3
  on_two_denied_prompts: suspend_push_and_require_passkey

The numbers are my starting suggestions, not published standards, except where a comment says otherwise. Chapter 8 turns this policy into the friction map, and shows how to tune each threshold against your own fraud data.

Note

If a user has no phishing-resistant factor yet, the step-up for a high-risk action should be a fallback factor plus a cool-down and a notification. Then offer a passkey on the spot. Never let the absence of a passkey become the reason a high-risk action goes through without friction.

Five rules that remove most MFA pain

  1. Never stack a code on top of a passkey. The passkey already proved two factors.
  2. Never prompt twice in one session for the same level of access.
  3. Tie step-up to named actions, not to time spent in the app.
  4. Rate-limit push and code requests, and investigate denied pushes, which CISA notes can signal a compromised password.
  5. Notify the user on every new device, new factor, and recovery event so the real owner can spot a takeover without being interrupted the rest of the time.

Rule 1 is the one I see broken most often. A team adds passkeys, keeps the existing MFA policy unchanged, and users get a face scan followed by an SMS code. The second step adds no security, doubles the friction, and teaches users that passkeys are slower. Fix the policy before you launch passkeys, not after.

How to know it is working

Track three numbers every month alongside passkey share of sign-ins:

  • Prompt rate for returning users. The share of returning sign-ins that see any challenge. Chapter 2 gives my working benchmark: a small single-digit percentage on a typical day.
  • Step-up completion rate. The share of step-up challenges the user passes on the first try. A low rate means the factor is wrong for the moment, or the prompt is badly explained.
  • Denied-prompt alerts. How many fired, and how many turned out to be real compromises. This is your early-warning feed.

For the risk-scoring side of this, Chapter 2 covers the signals. The enterprise view is in Adaptive Authentication from the enterprise passkey ebook.

Key takeaways

  • Push bombing works because frequent prompts make approval a habit. Fewer, meaningful prompts are a security control in their own right.
  • A denied prompt is a signal that the password is compromised. Alert on it and stop sending prompts to that account.
  • Remember trusted devices for around 90 days, keep sessions within NIST's 24-hour guidance, and require fresh proof only at named actions.
  • Make the step-up factor a passkey wherever possible, and never stack a code on top of one.
  • Write the step-up policy down in one file that product, security, and engineering all own.
Tip

Do this next week:

  1. List every place your product currently triggers MFA, and mark each one as either a named action or a timer.
  2. Turn off push without number matching, and set an alert on two or more denied prompts per account.
  3. Remove any second code that follows a passkey sign-in.
  4. Draft your step-up policy file using the sample above and review it with your fraud team.