There are several articles and blogs on the subject that describe how to block or limit an IP address from making repeated requests in order to reduce server load.
So, why do we need another blog or article to add to that list? What makes this article stand out from others?
- It uses Session State, which is the most recent item in current Asp.Net Core 3.1, rather than keeping the IP details in the database.
- It performs all operations outside of the action's logic, thus if the request exceeds the predetermined request count, the user will be denied access to the API's actions.
- It will become extremely fast in the process by utilizing session state and distributed memory concepts.
This post will go through a few key aspects that are commonly utilized while building APIs in ASP.Net Core. APIs are a sort of interface that connects a development system to a third-party system. APIs allow various programs to communicate with one another. On the other hand, as we all know, APIs are currently being used by single applications to manage frontend and backend communication. Overall, security is one of the most important characteristics to consider when creating APIs since no one knows who will use them or how they will be utilized.
This article will cover how to avoid a brute force attack and how to manage numerous requests from the client side. We'll also examine several key aspects of the ASP.Net Core Web API, such as Session State and built-in attributes. This article will show beginners and intermediates how to protect APIs from malicious third-party activity and reduce server load from repeated queries.
Step 1. Create a project.
To begin, build a new project or add functionality to an existing project to manage or handle web API requests. Create a project with the most recent version of the.Net framework. ASP.Net Core 3.1 is the most recent and stable version.
Step 2. Add and configure session services.
After successfully creating the project, the first step is to add session services to the Startup.cs file. As a result, we will be able to include session states in our app. Our goal is to detect client requests without relying on the database so that our program does not have to connect with the database every time. Also, we'll utilize session state storage, which will keep all client requests in distributed memory, making it incredibly fast, and preventing the user from entering the program if there are numerous requests in a particular time period. The following is a list of services that we'll need to include in our startup file.
services.AddDistributedMemoryCache();
services.AddSession();
Both of these will notify the system to keep the session items in memory for a long. We can also handle a session with a variety of settings such as timeout, httponly, and so on.
Now, in the Startup.cs file, add SessionMiddleware to the Configure section.
app.UseSession();
UseSession is a middleware component that enables the system to save items in a session state.
Step 3. Add a class to handle IP details.
Create a class that will handle and store request details. I've created a class called "IPDetailModel." This includes basic information about the incoming request.
public class IPDetailModel {
public string IPAddress {
get;
set;
}
public DateTime Time {
get;
set;
}
public int Count {
get;
set;
}
}
Step 4. Create a custom attribute class.
Now, in the project, create a class to handle the attribute logic. I named my class "TraceIPAttribute." After you've created a class, assign ActionFilterAttribute to it. Now, inside the class, add an "OnActionExecuting" method. The logic for handling an incoming request is shown below, and it determines if the request should go through the API logic or whether it has to be bypassed.
public class TraceIPAttribute : ActionFilterAttribute
{
IPDetailModel model = new IPDetailModel();
public override void OnActionExecuting(ActionExecutingContext context)
{
var remoteIp = context.HttpContext.Connection.RemoteIpAddress.ToString();
if (context.HttpContext.Session.GetString(remoteIp) == null)
{
model.Count = 1;
model.IPAddress = remoteIp;
model.Time = DateTime.Now;
context.HttpContext.Session.SetString(remoteIp, JsonConvert.SerializeObject(model));
}
else
{
var _record = JsonConvert.DeserializeObject<IPDetailModel>(context.HttpContext.Session.GetString(remoteIp));
if (DateTime.Now.Subtract(_record.Time).TotalMinutes < 1 && _record.Count > 1)
{
context.Result = new JsonResult("Permission denied!");
}
else
{
_record.Count = _record.Count + 1;
context.HttpContext.Session.Remove(remoteIp);
context.HttpContext.Session.SetString(remoteIp, JsonConvert.SerializeObject(_record));
}
}
}
}
So, as you can see from the code snippet, I've created a one-minute time slot with a single request count. As a result, if two or more requests from the same IP address are received within a minute, logic will stop a user from accessing the API's operations. The same IP address can access the APIs after one minute has passed.
The supplied code saves the incoming request in the IPDetailModel and then serializes and deserializes the data to get it from the modal. We may use the Newtonsoft.Json package to serialize and deserialize data.
After creating a custom middleware class, add it to the Startup.cs file's ConfigureServices section. This will inform the system of the presence of this middleware.
public void ConfigureServices(IServiceCollection services) {
services.AddControllers();
services.AddDistributedMemoryCache();
services.AddSession();
services.AddScoped < TraceIPAttribute > ();
}
Step 5. Add custom middleware to the controller side.
On the controller side, add the TraceIPAttribute. Before calling the controller's operations, it will analyze the request.
[ApiController]
[Route("[controller]")]
[ServiceFilter(typeof(TraceIPAttribute))]
public class WeatherForecastController : ControllerBase
{
// All actions and APIs will be placed here.
}
I've added the TraceIPAttribute to the controller because it'll be applied to all of the actions under it. However, if we want to use it for specific actions, we may assign it to those actions.
Understanding the flow of the process
When a request arrives, the TraceIPAttribute logic is executed first. If this is the first time the application has received a request, IPDetailModel will record the IP Address, time, and request count, and then it will give access to the controller's logic and return a response. However, if the same IP sends multiple requests, it will first go to the TraceIPAttribute logic, and if the request is from the same IP address and within the given time period, it will not allow the request to pass through the controller's actions, but will instead stop the request from moving forward, bypassing the requests from outside the controllers.
Conclusion
APIs are now the most common means of exchanging data between various systems and applications. Also, security attacks such as brute force attacks, man-in-the-middle attacks, and others are on the rise these days. As a result, this article will help to make your application secure against brute force attempts. Furthermore, if the server receives a large number of requests, the server's load might become extremely high, causing the application to go down. As a result, this approach will help in decreasing the load on the server as a result of the numerous requests from outside.