Client IP safelist for ASP.NET Core
What is client IP safe list technique
In this technique we track every request coming to our API and check its IP address, then we check list of our safe IP lists, if it exists in that list we allow it otherwise we don’t allow it to move on relevant controller.
Benefits of white listing IPs
This technique comes under security concerns, following are the benefits :
- Enhanced security
- Reduced attack surface
- Prevention of unauthorized access
At which level of application we should restrict it
We can apply this restriction at three levels:
1/ Cloud level ( e.g. using azure services )
2/ Network level ( e.g. firewall )
3/ Application level ( that we are going to do )
The more higher you can go would be more appropriate unless you have some IF/ELSE that forces you to come at low level.
Fun fact : We can apply whitelisting technique via load balancer or rate limiter as well.
Enough talking let’s dive into code implementation.
Ways to whitelist IP it in .NET API
We have two ways to achieve it:
1/ Via Middleware ( I already wrote about middlewares )
2/ Via Action Filters ( Read a previous newsletter of mine on action filters )
Before moving on let’s add list of allowed IPs in appsetting
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"SafeIPs": "127.0.0.1;192.168.1.5;119.156.228.255",
"AllowedHosts": "*"
}
1/ Implementing via Middleware: So let’ add the middleware, whose code looks like this
public class WhitelistIpMiddleware : IMiddleware
{
private readonly string _safelist;
private readonly ILogger _logger;
public WhitelistIpMiddleware(string safelist, ILogger logger)
{
_safelist = safelist;
_logger = logger;
}
public async Task InvokeAsync(HttpContext context, RequestDelegate next)
{
var remoteIp = context.Connection.RemoteIpAddress;
if (remoteIp == null)
{
_logger.LogWarning("Forbidden Request from IP: {RemoteIp}", remoteIp);
context.Response.StatusCode = StatusCodes.Status403Forbidden;
return;
}
var ipList = _safelist.Split(';');
var badIp = true;
if (remoteIp.IsIPv4MappedToIPv6)
{
remoteIp = remoteIp.MapToIPv4();
}
foreach (var address in ipList)
{
var testIp = IPAddress.Parse(address);
if (testIp.Equals(remoteIp))
{
badIp = false;
break;
}
}
if (badIp)
{
_logger.LogWarning("Forbidden Request from IP: {RemoteIp}", remoteIp);
context.Response.StatusCode = StatusCodes.Status403Forbidden;
return;
}
await next.Invoke(context);
}
}
And don’t forget to register the middleware
var safelist = builder.Configuration["SafeIPs"];
builder.Services.AddScoped<WhitelistIpMiddleware>(provider =>
{
var logger = provider.
GetRequiredService<ILogger<WhitelistIpMiddleware>>();
return new WhitelistIpMiddleware(safelist, logger);
});
2/ Via Action Filter: Let’s create an action filter that looks like this :
var safelist = builder.Configuration["SafeIPs"];
builder.Services.AddScoped<WhitelistIpMiddleware>(provider =>
{
var logger = provider.GetRequiredService<ILogger<WhitelistIpMiddleware>>();
return new WhitelistIpMiddleware(safelist, logger);
});
Register action filter service:
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.Logging;
using System;
using System.Linq;
using System.Net;
public class WhitelistIpActionFilter : ActionFilterAttribute
{
private readonly string _safelist;
private readonly ILogger<WhitelistIpActionFilter> _logger;
public WhitelistIpActionFilter(string safelist, ILogger<WhitelistIpActionFilter> logger)
{
_safelist = safelist;
_logger = logger;
}
public override void OnActionExecuting(ActionExecutingContext context)
{
var remoteIp = context.HttpContext.Connection.RemoteIpAddress;
if (remoteIp == null)
{
_logger.LogWarning("Forbidden Request from null IP address");
context.Result = new StatusCodeResult(StatusCodes.Status403Forbidden);
return;
}
_logger.LogDebug("Remote IpAddress: {RemoteIp}", remoteIp);
var ipSafelist = _safelist.Split(';');
bool isAllowedIp = false;
if (remoteIp.IsIPv4MappedToIPv6)
{
remoteIp = remoteIp.MapToIPv4();
}
foreach (var address in ipSafelist)
{
if (IPAddress.TryParse(address, out var testIp) && testIp.Equals(remoteIp))
{
isAllowedIp = true;
break;
}
}
if (!isAllowedIp)
{
_logger.LogWarning("Forbidden Request from IP: {RemoteIp}", remoteIp);
context.Result = new StatusCodeResult(StatusCodes.Status403Forbidden);
return;
}
base.OnActionExecuting(context);
}
}
Now we can apply this filter on any method in controller and even at controller level as well.
var safelist = builder.Configuration["SafeIPs"];
builder.Services.AddScoped(provider =>
{
var logger = provider.GetRequiredService<ILogger<WhitelistIpActionFilter>>();
return new WhitelistIpActionFilter(safelist, logger);
});
Find code of this newsletter issue at my GitHub Repo
Whenever you're ready, there are 3 ways I can help you:
- Subscribe to my youtube channel : For in-depth tutorials, coding tips, and industry insights.
- Promote yourself to 9,000+ subscribers : By sponsoring this newsletter
- Patreon community : Get access to all of my blogs and articles at one place