Skip to content
nviaOTP

Rate Limiting & Abuse Economics · Chapter 5 of 5

Protecting honest users: false positives and recovery paths

Every abuse control misfires sometimes. Measure the misfires, design graceful exits, and make sure the exit isn’t a new door for attackers.

5 min read

A rate limit that blocks one attacker and one hundred students on a campus network is not a success. Every control in this series has a false-positive rate, and honest users caught by it experience your product as broken. Worse, the escape hatch you build for them — a support form, an override, a fallback channel — can become the attacker’s preferred route. This final chapter is about measuring misfires, designing exits that keep honest users moving, and making sure those exits do not reopen what the earlier chapters closed.

Measure false positives directly

You cannot reduce what you do not measure. For each limiter and each consequence, track how often it fires and, separately, how often it fires on traffic that later proves legitimate. The cleanest proxy is eventual success: a user who hits a limit and then completes verification within the next hour through the normal path was probably honest. A user who hits a limit and never returns might be an attacker — or an honest user you lost.

-- Illustrative: limiter misfire proxy over 7 days
select limiter,
       count(*) as fired,
       count(*) filter (where succeeded_within_1h) as later_succeeded,
       round(100.0 * count(*) filter (where succeeded_within_1h) / count(*), 1) as misfire_pct
from limiter_events
where fired_at > now() - interval '7 days'
group by limiter
order by misfire_pct desc;
A misfire proxy. Limiters at the top of this list are the first candidates for higher thresholds or better keys.
The graceful exit. The last box closes the loop: every misfire is data for making the limiter smarter.

Attacker view

Exploiting your exits

  • Your “can’t receive code?” link skips the strong factor.
  • Support will reset MFA for a convincing story.
  • Allow-listed networks get no limits at all.
  • Fallback channels have looser limits than the primary.

Defender view

Exits that stay closed to me

  • Alternatives must meet the same assurance level as the blocked path.
  • Support follows the recovery policy, with holds and notification.
  • High-population sources get higher thresholds, not exemptions.
  • Every channel shares the same per-account and per-challenge budgets.

Design the exits

When an honest user hits a limit, the best outcome is that they succeed through an alternative of equal strength. If they have a passkey, offer it. If they have a TOTP app and the SMS send was limited, offer the app. If they have neither, show a clear countdown and explain what will happen. Only when no equivalent path exists should the exit lead to recovery, and recovery must carry the holds and notifications from series 3. Every exit should be listed in the threat register as a path with its own severity.

Copy matters more than engineers expect. Messages that blame the user (“you entered too many wrong codes”) increase support contacts; messages that explain and offer a next step (“for your security we’ve paused new codes for two minutes — you can also sign in with your passkey”) reduce them. Quillon measured, as an illustrative example, a drop in support tickets after rewriting four limiter messages, with no change to the limits themselves.

Known populations

Some honest users are predictably affected: people on shared networks, travellers whose numbers roam, users with accessibility needs who type slowly or make more errors, and users in regions where messages arrive late and they request a second code. Handle them with policy, not exceptions. Higher per-IP thresholds for known NAT ranges, longer code lifetimes for destinations with high observed latency, and generous per-challenge budgets help all of them without opening a bypass.

  • Campus and enterprise NAT: raise per-IP thresholds, keep per-account and per-challenge budgets.
  • Roaming users: do not treat a new country alone as fraud; combine with other signals.
  • Slow typists: per-challenge budget of five or more, clear remaining-time indicator.
  • Late messages: accept the previous code briefly after a resend, then invalidate it.

The last item is worth explaining. When a user requests a second code because the first was late, the first often arrives seconds later and they type it. Rejecting it creates a frustrating loop. Accepting the previous code for a short grace period after a resend — say sixty seconds — while still counting attempts against the same budget removes the loop without materially changing the attacker’s odds.

Close the loop

Hold a monthly review of limiter misfires alongside abuse incidents. For each limiter, compare the misfire rate with the attacks it stopped. Raise thresholds or change keys where misfires dominate; tighten where attacks got through. Marit Okwu, Quillon’s support lead, joins these reviews with a sample of tickets, which often reveal misfire patterns the metrics miss — such as a single carrier delaying messages in one region on 2026-10-11, which caused a wave of resend cooldowns.

This is the same discipline as the threat register in series 3: every control has an owner, a metric, and a review. The difference is that here the metric measures harm to your own users. A mature abuse programme reports both numbers side by side, and treats a rising misfire rate as an incident in its own right.

Treat the false-positive dashboard as a shared artefact. When product, support and security all look at the same numbers each month, arguments about whether a limit is too strict become conversations about data rather than opinion, and the limits improve faster.

Finally, celebrate fixes. When a threshold change cuts misfires without letting attacks through, share it, so teams see that protecting users and stopping abuse are the same job.

0/3 correct · 0 answered

  1. 1. What is a practical proxy for a limiter false positive?

  2. 2. When an honest user hits a limit, the exit should lead to…

  3. 3. Why prefer higher thresholds over exemptions for office IPs?

Discussion

Comments are public and shared with every reader. No account needed — just a display name. Comments reported by three readers are hidden automatically.

15 more characters needed

Loading comments…