Who cares about rate and resource limiting?

Rate limiting is crucial but often overlooked in security. This article explores its importance for all endpoints, not just login forms, using a real-life penetration test story. Learn how comprehensive rate limiting can protect your application from attacks and ensure stability.

3 months ago   •   7 min read

By Stephen Rees-Carter
Table of contents

Rate and resource limiting is one of the topics you’ll typically find on ‘security checklists’, but it’s almost always a brief mention - like everything else on the list. So the question you’re left with is: why do I need rate limiting? What does it actually do? 

To help answer this question, I want to tell you a story from a penetration test I did recently. It highlights to me perfectly why rate limiting is so important, and why you need to consider it for every endpoint - rather than just the obvious login form!

Story Time

As part of a new penetration test, I went to the client’s login form and proceeded to type in the wrong password multiple times. It took 5 attempts within a few seconds before I was hit with a familiar error:

“Invalid credentials, please wait 55 seconds before trying again.”

This boded well for my client. They had rate limiting on their login form! This encouraged me that my client took security seriously, and I was hopeful for good things to come.

Next up, I clicked on my trusty VPN to change to a new IP address and tried again:

“These credentials do not match our records.”

Oh… 

So it looks like my client is rate limiting based on IP address. Unfortunately, rotating IP addresses is trivial. VPNs are ridiculously cheap, Tor is free, and botnets sell access to millions of hijacked IP addresses.

Disheartened, I punched in a few more wrong passwords to get the rate limiter block back and then changed to a different user account… and the rate limiter was gone.

This told me one very important thing: The rate limiter is based on both the email address and the IP address.

Armed with this knowledge, it would be trivial for an attacker to conduct a brute-force or credential stuffing attack on my client, rotating email addresses and IP addresses to bypass the rate limiter.

What are brute-force and credential stuffing attacks?

As a quick aside, brute-force attacks are when an attacker tries to guess the user’s password by trying lots of possible passwords. Either by randomly generating passwords from a character set (i.e. all possible 8-character strings with the letters a-z), or using a list of popular passwords and cycling through those in case the target user has used a common password.

Credential stuffing attacks are where the attacker has a list of known working email address and password combinations from other breached websites. The attacker will try each combination of email-password to see if any work on the targeted site.

Credential stuffing is an incredibly effective attack because users often reuse passwords between different services, giving a good success rate with a small number of requests needed. Unlike brute-force attacks, which require a significant amount of requests to find a working password.

This is the reason why Troy Hunt built the Pwned Passwords service in Have I Been Pwned.

Back to the story…

At this point in my penetration test, I was looking at a login form with limited rate limiting. It would be easy to run a credential stuffing attack, rotating IP addresses after every 4 attempts (or whatever the rate limiter was set to).

I wrote up the weakness for my client and proceeded to log in correctly, to be hit with this:

Two-Factor Authentication prompt

Suddenly I felt better! My client had Two Factor Authentication (2FA) configured on their app! 

We just talked about rate limiting and credential stuff attacks, and it’s worth acknowledging at this point that even with really solid rate limiting in place, credential stuffing attacks are still incredibly effective. It is difficult to block credential stuffing attacks with rotating IP addresses. 

But I’m supposed to be convincing you that rate limiting is important, so let’s leave that thought there and get back to my story!

I was at the 2FA prompt and it was asking for my code - a second layer of defence in case the password is breached. So I typed in “000000” and clicked enter:

“The provided authentication code was incorrect.”

Then I typed in “000001” and clicked enter: “The provided authentication code was incorrect.”.

Then I typed in “000002” and clicked enter: “The provided authentication code was incorrect.”.

Then I typed in “000003” and clicked enter: “The provided authentication code was incorrect.”.

Then I typed in “000004” and clicked enter: “The provided authentication code was incorrect.”.

Then I typed in “000005” and clicked enter: “The provided authentication code was incorrect.”.

Then I typed in “000006” and clicked enter: “The provided authentication code was incorrect.”.

Then I typed in “000007” and clicked enter: “The provided authentication code was incorrect.”.

Then I typed in “000008” and clicked enter: “The provided authentication code was incorrect.”.

Then I typed in “000009” and clicked enter: “The provided authentication code was incorrect.”.

Then I typed in “000010” and clicked enter: “The provided authentication code was incorrect.”.

Then I typed in “000011” and clicked enter: “The provided authentication code was incorrect.”.

This was bad!

Ok, I tell a lie….

I’m a penetration tester, so I was incredibly excited at this point. I hurriedly switched to my hacking toolkit, configured a brute-force attack, and within a few minutes I’d sent a ridiculously large number of requests at my client server… and found a valid 2FA code. I was in!

There was no rate limiting on the 2FA prompt. Absolutely nothing stopping me from sending as many requests as I wanted and guessing a valid 2FA token. It didn’t matter that the login form itself had rate limiting, the 2FA prompt was missing it - give gave me a working attack vector.

As I mentioned above, it’s still tricky to block things like credential stuffing attacks on login forms - because you can’t easily identify the user - this is not an issue on a page like the 2FA prompt. The user has been identified through their password, so you can rate limit based entirely on the user, and limit 2FA attempts across all IPs to a small number. This would completely prevent this vulnerability, and is what my client did as soon as I reported the issue.

Rate Limiting and APIs

In the same way that rate limiting was missing on the 2FA prompt - after the password had been verified - we need to think about rate limiting our APIs beyond the authentication flows too!

For starters, your users are often authenticating with API keys - which are practically impossible to brute force - so you need to think about all of your endpoints, and apply rate limiting across your entire API.

Malicious activity is still very much a concern, as it is with interfaces, but the nature of APIs opens them up to unintentional activity that can have negative impacts on your API and your service in general.

Consider a client application which loads a list of records on screen, and the developer accidentally wrote it so each record would be loaded via a separate API request. If the page tries to load 1,000 records, and there are 100 active users all trying to access the page at the same time, your API will get hit with 100,000 requests within a minute. 

Can your API handle that, or will it slow down, or even crash?

If you implement a relaxed rate limiter across your API, and limit it to something like 50 requests per second, that page loading 1,000 records won’t be able to send 100,000 requests. You might get 5,000 instead.

In this scenario, the presence of the rate limit will encourage (or force) the developer to optimise their client app to optimise its API requests - or it will stop working when users are trying to use it.

You don’t need to set strict limits, but you do need some limits.

Malicious Activity

The other aspect is using rate limiting, and resource limiting for that matter, to prevent malicious activity. There are a few main areas of concern:

Preventing Denial of Service attacks

This is where an attacker intentionally makes resource-expensive API requests to slow down or crash the service. It’s far simpler to send 10 requests to a slow endpoint, than 1,000,000 to a fast endpoint, so you need to be aware of the risk and prevent it from being abused.

Prevent Data Scraping

Data breaches often occur when APIs endpoints are left unprotected, without limits or proper authorisation, and the attacker is able to scrape (download) a huge amount of data from the exposed endpoints. Ignoring the missing authorisation issue, if your API endpoints have rate limits, making thousands or even millions of requests to download large amounts of data becomes virtually impossible. 

Limit Costs

Ok, so it’s not normally caused by malicious activity, but it’s closely related to the others and has a significant impact, so it’s worth mentioning here too.

Running an API costs money: you need to pay for the infrastructure, and any third-party services you integrate with. If you allow your users to make as many requests as they like, whenever they like, there is the potential for them to use a lot more resources and bump your costs up quite significantly. Especially if you’re paying for an expensive third-party integration!

Monitoring APIs

It’s all well and good to say you need to add in rate and resource limiting, but sometimes you don’t know where you need them. Sensitive routes like authentication are obvious, but how do you know which are your resource intensive routes, so you can add limits to them too?

This is where you need something like Treblle's API Observability, which allows you to monitor exactly what’s happening on your API. It’ll tell you the slow routes and the popular routes, so you can add in resource limiting to keep things running smoothly. Plus maybe highlight areas where you can optimise your APIs or document more efficient methods for your clients. Alongside this, Treblle's API Security can help monitor your API for anything you should be aware of, security-wise.

Summary

Rate and resource limiting is important to prevent abuse and malicious activity, and needs to be implemented on more than just the borders of your application, and more than just the basic routes.

Consider what the route does and what impacts there could be if an attacker makes a lot of requests quickly. Can your application handle the load, and what will it cost?

💡
Switch to Treblle for seamless API monitoring and robust security. Enhance your application's performance and protect it from attacks with our comprehensive rate limiting solutions.

Spread the word

Keep reading