Rate limits that don't leak: throttling without building an oracle
A rate limiter is supposed to slow an attacker down. Built carelessly, it does the opposite: it tells them which guesses were close. The defense and the leak are often the same line of code.
Security Practice
Throttling looks like one of the simplest controls to add: count requests, reject the ones over the line. But a lookup endpoint that protects a short code or a login has a second job that is easy to forget. It must slow the attacker down without telling them anything. Get the second part wrong and the throttle you added to stop brute force becomes the signal that guides it.
How a limiter turns into an oracle
Imagine a recall endpoint that returns a fast not-found for an unknown code, but rate-limits more aggressively once a real code is presented with the wrong second factor. An attacker does not need to break in to learn from that. The difference in how the endpoint responds, slower here, a different status there, a count that ticks differently, is itself the answer. They are no longer guessing in the dark; the system is grading their guesses.
- Distinguishable responses. A 404 for a bad code and a 429 for a real-code-wrong-factor lets an attacker separate the two and focus only on codes that exist.
- Counters scoped to truth. A limiter keyed on whether the record exists leaks existence through its own behavior, even if the body says nothing.
- Timing tells. An early return on the cheap path and a slow comparison on the real one is a side channel, regardless of what either response says.
Building one that stays quiet
The fix is to make the observable behavior depend on the caller, not on the secret. We rate-limit on what the attacker controls, their address and the code they typed, and we keep the response shape uniform whether or not that code was ever real. A miss and a near-miss come back the same way, on the same path, in the same time.
- 01Key the limit on the caller and the input, never on whether the lookup matched. The limiter must not know more than the attacker does.
- 02Return one uniform shape for every failure, so not-found and wrong-second-factor are indistinguishable from outside.
- 03Use constant-time comparison on the secret parts, so the duration of a response is not a clue to its content.
- 04Add a lockout for sustained abuse, applied to the caller, so the cost of trying climbs while the information gained stays at zero.
A good rate limiter raises the cost of guessing. A great one makes sure each guess teaches the attacker nothing.
This is the same discipline as everywhere else in a high-stakes system: assume the person on the other end is studying your responses, and make sure the responses have nothing to say. The throttle is the obvious half of the control. The silence is the half that actually protects the record.