Every web application is vulnerable to a type of attack in which a malicious third party repeatedly connects to your web server and/or makes bulk HTTP requests, consuming resources and preventing other legitimate requests from being processed. This is called a Denial of Service (DoS) attack and all web applications are susceptible regardless of the technology used to create the application because there is overhead associated with accepting and handling an http request even if the request itself is not valid.
For those who host an application that is publicly accessible and also want it to be highly available, being able to handle DoS attacks is critical. There are many methods for effectively handling DoS attacks, but as a first step, all web apps should implement rate limiting.
What is Rate Limiting?
Rate Limiting is the process of monitoring and, in some cases, restricting the amount of requests an application handles. This is critically important to any application that accepts requests from unknown hosts (e.g. is not restricted to known actors at the networking level via IP whitelisting or something similar).
Rate Limiting Methodology
The specifics of implementing rate limiting can vary quite a bit, but typically you need to identify some aspect of a given request which you can use to group requests into buckets, count requests with that given identifier over a period of time, and then set parameters on how frequently requests with that identifier can be made. For example, you could limit requests from a specific IP address or requests made with a specific credential.
Rate limiting on a specific user credential (e.g. a specific issued token or a user’s unique identifier) can be useful for preventing many concurrent requests to an endpoint that requires authentication, but this would not prevent a DoS attack targeting an authenticated endpoint. To prevent this scenario, limiting requests based on the host IP address that is making the request would make more sense.
How Can Malicious Actors Get Around Rate Limiting?
Usually, it is a good idea to rate limit requests by IP address, but a malicious actor can still get around this by executing a Distributed Denial of Service attack (DDoS) – a modified form of a DoS attack in which the bulk requests are sent from many distributed hosts. In this scenario, rate limiting on an IP address would be ineffective as the source IP addresses for the requests involved in the attack would not be the same.
There are different types of DoS attacks that can be executed on different layers, which makes them very hard to guard against comprehensively. Any rate-limiting functionality we implement within our application code can only prevent rate-limiting attacks that are designed to attack our application on the application layer.
Mitigating Distributed Denial of Service (DDoS) Attacks?
Having multiple strategies within an application for rate limiting can mitigate the risk of a DoS attack, but ultimately, if you want to be able to handle DDoS attacks from a determined attacker, then you will likely need to protect your app with a third-party solution. Cloudflare’s DDoS Protection is one option, or if your application is hosted in a cloud provider like AWS/Azure/GCP, those providers will have their own solutions for DDoS protection as well.
Using a third-party solution for DDoS protection can be highly beneficial because these systems are usually highly distributed, resilient, and capable of detecting DDoS attacks that your application would never be able to catch and filter out dynamically.
How to Implement Rate Limiting in an Application
Most frameworks for building a web application have built-in functionality or external modules that can do most of the heavy lifting for us. In my framework of choice, ASP.NET Core, we have built-in tools for rate limiting that make it a breeze to implement. Consider this barebones C# startup code for a simple web application:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
var app = builder.Build();
app.UseHttpsRedirection();
app.UseRouting();
app.UseRateLimiter();
app.MapControllers();
app.Run();
To add simple IP-based rate limiting to this application, we would add the configured rate-limiting services to our application and then add the rate-limiting middleware to the http pipeline. Here’s our barebones application from before with IP-based rate limiting added:
using System.Net;
using System.Threading.RateLimiting;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
builder.Services.AddRateLimiter(options =>
{
options.RejectionStatusCode = StatusCodes.Status429TooManyRequests;
options.GlobalLimiter = PartitionedRateLimiter.Create<HttpContext, IPAddress>(httpContext =>
RateLimitPartition.GetFixedWindowLimiter(
partitionKey: httpContext.Connection.RemoteIpAddress,
factory: key => new FixedWindowRateLimiterOptions
{
PermitLimit = 15,
Window = TimeSpan.FromMinutes(1),
QueueLimit = 0
})!);
});
var app = builder.Build();
app.UseHttpsRedirection();
app.UseRouting();
app.UseRateLimiter();
app.MapControllers();
app.Run();
In our example, we have set up a partitioned rate limiter using the remote IP address as our partition key, preventing more than 15 requests per minute from the remote source.
How to Test Our Application with a Simple DoS Attack
There are lots of dedicated tools that can be used specifically for executing a DoS attack, but we can most easily simulate this sort of attack by using a stress testing tool like k6. Here is a simple K6 test file designed to flood our application with GET requests to a given endpoint over a 30-second testing window:
import http from 'k6/http';
export const options = {
duration: '30s',
};
export default function () {
const url = '<the endpoint you want to hit goes here>';
http.get(url);
}
We can execute this file by calling the k6 application from the command line and passing it our test file:
k6 run test.js
We can see once the process is finished that exactly 15 requests succeeded while the rest were failures – exactly what we expected based on our configured rate-limiting policy.
Conclusion
Implementing rate limiting in your web application is a great idea to discourage bad actors, but it’s not a silver bullet. In most scenarios where uptime is important, it’s advisable to have rate limiting implemented and utilize third-party tools to save your application’s resources as well as catch malicious attacks that you wouldn’t be able to prevent with rate limiting.
If you want to learn more about how to detect and defend against malicious attacks, take a look at the Practical SOC Analyst Associate certification, which will train and test your blue team abilities. If testing web applications for vulnerabilities with methods like DoS attacks is where your interests lie, then the Practical Web Pentest Associate certification will teach you the tools and methods for web app pentesting before putting you up against a real-world assessment of those skills. Choose your path today!
About TCM Security
TCM Security is a veteran-owned, cybersecurity services and education company founded in Charlotte, NC. Our services division has the mission of protecting people, sensitive data, and systems. With decades of combined experience, thousands of hours of practice, and core values from our time in service, we use our skill set to secure your environment. The TCM Security Academy is an educational platform dedicated to providing affordable, top-notch cybersecurity training to our individual students and corporate clients including both self-paced and instructor-led online courses as well as custom training solutions. We also provide several vendor-agnostic, practical hands-on certification exams to ensure proven job-ready skills to prospective employers.
Pentest Services: https://tcm-sec.com/our-services/
Follow Us: Email List | LinkedIn | YouTube | Twitter | Facebook | Instagram | TikTok
Contact Us: [email protected]
See How We Can Secure Your Assets
Let’s talk about how TCM Security can solve your cybersecurity needs. Give us a call, send us an e-mail, or fill out the contact form below to get started.