Rate Limiting: The Control Everyone Agrees On and Nobody Implements
Among the sea of validations and encryption, one of the most critical things – and still one of the least implemented – is rate limiting. Any API, no matter how closed and controlled it is, can become vulnerable the moment you give up on rate limiting. Especially when it has methods that are sensitive to repeated use at high speed.
I’ll start with an example I ran into. A standard registration site, where every POST /register creates a new user and sends an email. For months, nobody minded that there was no limit on the number of requests per minute. Until one day a simple bot fired tens of thousands of requests, flooded the mail, created load on the database, and above all – harvested hundreds of real usernames out of the API’s own responses (username taken, and so on).
But the problem is not only brute-force attacks. Without a rate limit, any scraper or attacker can simply run in the background, check whether users exist, run a dictionary attack against weak passwords, or test whether a leaked token is still valid.
The bypasses you should expect
A popular one: many systems check the rate by IP, but ignore headers like X-Forwarded-For, or fail to count users holding a different session or token. The attacker spins up dozens of proxies, changes a header, or simply opens dozens of dummy accounts – and walks past any simple check.
What a correct implementation looks like
Rate limiting has to be checked at several layers – IP, session, user, and even the specific action path (a particular endpoint). In practice a proper implementation includes:
- Counters updated immediately – in real time, not once a minute.
- A block for several minutes after the threshold is crossed.
- An alert on an unusual excursion – a pattern that is not normal.
- An error message that does not give too much away. Not “you were blocked because you tried too many times”, but something generic.
- Edge-case testing on every API – including internal ones, not only the external ones.
What to test for in a penetration test
- Can you send 10,000 requests across 10 different
sessionIds without being blocked? - What happens if you change the IP header?
- Is the rate limit preserved on
POSTas well asGET? - Is it applied to endpoints that were never meant for the front end?
I have seen enormous systems fall over this – right up until a bot arrived and exploited exactly the same small hole nobody had checked.
And a general recommendation from me: do not settle for an external library. Always check its integration with the business logic of your own system.
I first shared a version of this as a LinkedIn post on 2025-07-21. It is republished here, lightly edited, so it is easier to find and reference. — Erez Metula
