Preventing MFA Fatigue and Push Bombing

Push-based second factors fail in a way that codes do not: an attacker holding a valid password can simply keep asking, and eventually somebody taps approve to make their phone stop buzzing. This page is part of the multi-factor authentication with TOTP and FIDO2 guide and covers the controls that close it.

The Attack Is a User-Experience Flaw

The mechanics are trivial. The attacker has the password — from a breach, a phishing page, or reuse — and triggers authentication repeatedly. Each attempt sends a prompt to the real user’s device. Some fraction of users, at some hour of the night, will approve one: because they assume it is a glitch, because they were logging in themselves at that moment, or simply to stop the notifications.

Why repeated approval prompts eventually succeed The attacker repeats the login attempt, each attempt pushes a prompt with no context to the user's device, and a single approval grants a session; the factor itself never fails cryptographically. Attacker retries password already known Prompt with no context "Approve sign-in?" One tap at 2am, to stop it Session granted Nothing here is a cryptographic failure — the factor worked exactly as designed.
The vulnerability is the prompt: a binary question, with no context, that costs the attacker nothing to ask again.

Number Matching Removes the Free Guess

The single most effective change is to stop the prompt being answerable without looking at the screen that started the login. Display a two- or three-digit number on the login page and require the user to select or type it on their device.

An attacker triggering a login from their own machine sees their own number, but the user’s device shows a choice — and the user has no way to know which to pick unless they are the one looking at the login screen. A reflexive tap is no longer possible, because there is nothing to tap.

Show context alongside the number: the application, the approximate location, the device and the time. Context does not stop a determined approval, but it converts “something is asking me to approve” into “something in another country is asking me to approve”, which is a question people answer correctly far more often.

Rate Limiting, Backoff and Lockout

Number matching removes the easy path; limits remove the attempt volume that makes fatigue possible in the first place.

Layered limits on second-factor prompts Limit prompts per account per minute, apply increasing delays after consecutive denials, lock the factor after a threshold and notify the user, and alert operations on any account exceeding the pattern. Per-minute cap a few prompts per account Backoff each denial costs more time Lock the factor after a threshold and tell the user Alert repeated denials are a compromise signal
A denied prompt is one of the highest-signal events in an authentication system: the user has explicitly said "this was not me".

Treat a denial as more than a failed attempt. A single explicit denial should raise the cost of the next attempt substantially; two or three should lock the factor, end any sessions for that account, and notify the user through a channel the attacker is unlikely to control. The password is presumed compromised at that point, so prompting for a reset is appropriate rather than alarmist.

Count attempts per account rather than per address. An attacker rotating through addresses defeats an address-based limit trivially, and the account is the thing being attacked.

The Structural Fix

Number matching and rate limits make the attack expensive. Moving to a phishing-resistant factor removes it, because there is no prompt to spam: a passkey or security key requires the user to be on the real login page with their authenticator, and an attacker with only a password has nothing to trigger.

That is the direction to travel, and the passkeys walkthrough covers the rollout. In the meantime, treat push approval as a factor that needs its own hardening rather than as an equivalent to a hardware key — and never let a push approval alone satisfy a step-up check for a high-value operation.

Keep the fallback ladder honest too. If push can be bypassed with an SMS code when the phone is unreachable, the account’s real strength is SMS, and every control above it is decoration.

Designing the Prompt Itself

The prompt is a security control with a user interface, and small wording choices change how often it is answered correctly.

Lead with the decision, not the brand. “Approve sign-in?” invites a reflex; “Someone is trying to sign in from Frankfurt on Windows — is this you?” invites a judgement. Put the number-matching challenge above the buttons so the user cannot act before reading, and make the deny action at least as prominent as approve, because the safe answer should never require more effort than the unsafe one.

Give denial a consequence the user can see. A prompt that says “if this was not you, deny and we will lock your account and help you reset your password” tells the user that denying is useful rather than merely dismissive. Users who believe denial does nothing tend to approve instead, on the theory that the notifications will otherwise continue.

Avoid prompting when you do not need to. Every unnecessary prompt trains users to approve without reading — which is exactly the reflex the attack exploits — so use device recognition and risk signals to keep routine sign-ins on a familiar device from generating one. The prompts that remain then carry information simply by existing.

Finally, make the prompt expire quickly. A challenge that stays actionable for ten minutes gives an attacker a long window in which the user might wander back to their phone and clear notifications carelessly; sixty to ninety seconds is enough for a legitimate login and short enough to make opportunistic approval unlikely.

Detecting the Attack in Progress

Two shapes of push-bombing traffic A burst of many prompts within minutes is caught by a per-minute rate alert, while a slow steady trickle over hours is only caught by a per-account daily count. Burst caught by a per-minute rate alert Slow trickle needs a daily count Alert on both shapes, or the patient attacker stays under every threshold you set.
The slow variant is deliberately designed to sit below a rate alert, which is why the daily count is not redundant.

Push bombing is one of the most detectable attacks in authentication, because its signature is unusual by construction: many prompts for one account in a short period, from a source the account has never used, with denials or timeouts rather than approvals.

Alert on the pattern rather than on any single event. Count prompts per account per five minutes, and page when it exceeds a threshold no legitimate user reaches. Include the source address, the user agent and the geography, so the responder can tell immediately whether this is an attack or a user with a broken client retrying.

Watch for the quieter variant too: a low, steady rate of prompts spread over hours, designed to stay under a naive threshold. A per-account daily count catches that where a per-minute rate does not, and the two together cover both shapes.

Feed confirmed cases back into the password side. An account under push bombing has a compromised password by definition, so the response includes forcing a reset and checking whether that password appears in a known breach corpus — otherwise you have stopped this attempt and left the credential that enabled it in place for the next one.

Frequently Asked Questions

Is number matching enough on its own?

It removes the reflexive approval, which is most of the attack, but it does not stop an attacker who is on the phone to the user talking them through it — a technique that has worked repeatedly in real incidents. Pair it with rate limits, context in the prompt, and alerting on denials, and treat phishing-resistant factors as the destination rather than an optional upgrade.

How many prompts should an account be allowed?

Very few — three to five per account in a short window is generous, because a legitimate user rarely needs more than one or two. Apply increasing delays after each denial, and lock the factor after a small number of explicit denials. The cost of being slightly too strict is a support contact; the cost of being too permissive is an approved login the user did not intend.

What should happen when a user denies a prompt?

Treat it as a reported compromise, not a failed login. End other sessions for the account, block further prompts for a period, notify the user through a separate channel with details of the attempt, and prompt for a password change — because a denial means someone else had the password. Logging it and moving on wastes the clearest signal you will ever get.

Should we remove push approval entirely?

Eventually, in favour of phishing-resistant factors, but a sudden removal usually pushes users onto something weaker such as SMS. The workable path is to make passkeys the default, keep hardened push with number matching as a fallback, and remove push only when adoption makes it unnecessary — while never letting it satisfy the highest-assurance operations in the meantime.

Does this affect time-based codes as well?

Codes are not vulnerable to fatigue in the same way, because there is no prompt to spam — the user must actively read a code. They remain vulnerable to real-time relay through a phishing proxy, which number matching does not address either. Different attack, same conclusion: the durable fix is an origin-bound factor.

Treat every one of these controls as a stopgap on the way to origin-bound factors, and the roadmap writes itself.