Implementing a Client White-list using ASP.NET Core Middleware

This article shows how a client safe-list could be implemented using ASP.NET Core middleware checking the Remote IP address of the request. If the client IP is on the safe-list, no restrictions exist.

Code: https://github.com/damienbod/ClientIpAspNetCoreIIS

History

2018-08-30 Updated to .NET core 2.1 and added support for IP6

The middleware uses an admin white-list parameter from the constructor to compare with the remote ip address from the HttpContext Connection property. This is different to previous versions of .NET. In the example, all GET requests are allowed. If any other request method is used, the remote IP is used to check if it exists in the safe-list. If it does not exist, a 403 is returned.

using System;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;

namespace ClientIpAspNetCore
{
    public class AdminSafeListMiddleware
    {
        private readonly RequestDelegate _next;
        private readonly ILogger<AdminSafeListMiddleware> _logger;
        private readonly string _adminSafeList;

        public AdminSafeListMiddleware(RequestDelegate next, ILogger<AdminSafeListMiddleware> logger, string adminWhiteList)
        {
            _adminSafeList = adminWhiteList;
            _next = next;
            _logger = logger;
        }

        public async Task Invoke(HttpContext context)
        {
            if (context.Request.Method != "GET")
            {
                var remoteIp = context.Connection.RemoteIpAddress;
                _logger.LogDebug($"Request from Remote IP address: {remoteIp}");

                string[] ip = _adminSafeList.Split(';');

                var bytes = remoteIp.GetAddressBytes();
                var badIp = true;
                foreach (var address in ip)
                {
                    var testIp = IPAddress.Parse(address);
                    if(testIp.GetAddressBytes().SequenceEqual(bytes))
                    {
                        badIp = false;
                        break;
                    }
                }

                if(badIp)
                {
                    _logger.LogInformation($"Forbidden Request from Remote IP address: {remoteIp}");
                    context.Response.StatusCode = (int)HttpStatusCode.Forbidden;
                    return;
                }
            }

            await _next.Invoke(context);

        }
    }
}

The safe-list is configured in the appsettings.config. This is a ‘;’ separated list which is split in the middleware class.

{
  "AdminSafeList": "127.0.0.1;192.168.1.5;::1",
  "Logging": {
    "IncludeScopes": false,
    "LogLevel": {
      "Default": "Debug",
      "System": "Information",
      "Microsoft": "Information"
    }
  }
}

In the startup class, the AdminSafeListMiddleware type is added using the appsettings configuration.

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
	...

	app.UseStaticFiles();

	app.UseMiddleware<AdminSafeListMiddleware>(Configuration["AdminSafeList"]);
	app.UseMvc();
}

If a request is sent, other that a GET method, and it is not in the safe-list, the 403 response is returned to the client and logged.

2016-12-18 16:45:42.8891|0|ClientIpAspNetCore.AdminWhiteListMiddleware|INFO|  Request from Remote IP address: 192.168.1.4 
2016-12-18 16:45:42.9031|0|ClientIpAspNetCore.AdminWhiteListMiddleware|INFO|  Forbidden Request from Remote IP address: 192.168.1.4 

An ActionFilter could also be used to implement this, for example if more specific logic is required.

using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc.Authorization;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.Logging;

namespace ClientIpAspNetCore.Filters
{
    public class ClientIdCheckFilter : ActionFilterAttribute
    {
        private readonly ILogger _logger;

        public ClientIdCheckFilter(ILoggerFactory loggerFactory)
        {
            _logger = loggerFactory.CreateLogger("ClassConsoleLogActionOneFilter");
        }

        public override void OnActionExecuting(ActionExecutingContext context)
        {
            _logger.LogInformation($"Remote IpAddress: {context.HttpContext.Connection.RemoteIpAddress}");

            // TODO implement some business logic for this...

            base.OnActionExecuting(context);
        }
    }
}

The ActionFilter can be added to the services.

public void ConfigureServices(IServiceCollection services)
{
	services.AddScoped<ClientIdCheckFilter>();

	services.AddMvc();
}

And can be used specifically on any controller as required.

[ServiceFilter(typeof(ClientIdCheckFilter))]
[Route("api/[controller]")]
public class ValuesController : Controller

Note: I have not tested this with all the different possible hops, forward headers. Only tested with IIS and kestrel.

Links:

https://docs.microsoft.com/en-us/aspnet/core/fundamentals/middleware

http://odetocode.com/blogs/scott/archive/2016/11/22/asp-net-core-and-the-enterprise-part-3-middleware.aspx

Advertisements

5 comments

  1. […] fast & secure by Muhammad Rehan Saeed. ASP.NET Core response optimization by David M Pine. Implementing a client white-list using ASP.NET Core middleware by Damien Bod. MVC Areas with ASP.NET Core by Josh Morales. SEO friendly URLs for ASP.NET Core by […]

  2. […] Implementing a Client White-list using ASP.NET Core Middleware […]

  3. […] pequisa rápida e descobri um código bem interessante feito pelo Damien Bod e que você pode ver clicando aqui. Da uma visita la, alem da referencia é bom visitar […]

  4. Nice article. Looks like your approach is the same as on the official Microsoft page (https://docs.microsoft.com/en-us/aspnet/core/security/ip-safelist?view=aspnetcore-2.1). There’s also a project called Firewall which can do IP filtering, which is mentioned here: https://dusted.codes/asp-net-core-firewall

    1. thanks, yes this article got ported to the MS Docs. yes, IP Filtering would be a great improvement.

      Greetings Damien

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d bloggers like this: