Only accept local request in ASP.Net Core 2.1 MVC

Viewed 3863

I have a simple ASP.Net Core 2.1 MVC app and in one of the controllers, I would like to implement an action that only accepts requests from local (i.e. request originates from 127.0.0.1, or from the same address as the server's IP).

I've been looking for a filter in ASP.Net Core that is suitable for this purpose but can't seem to find one. I can use an AuthorizeAttribute, e.g. [Authorize(Policy = "LocalOnly")]

and registering the corresponding policy in ConfigureServices in Startup:

services.AddAuthorization(options =>
{
    options.AddPolicy("LocalOnly", policy =>
    {
        policy.RequireAssertion(context =>
        {
            if (context.Resource is AuthorizationFilterContext mvcContext)
            {
                return mvcContext.HttpContext.Request.IsLocal();
            }
            return false;
        });
    });
});

where IsLocal() is an extension method of HttpRequest.

However I don't think this is the right way to do it -- what I'm trying to do is not actually authorization, and since I don't have authentication in my program, the error produced isn't correct either.

Is there a simple and legit way to do what I want with filters? Or is this actually something that should be done in the action logic in controllers? Or perhaps this whole idea of checking for local request isn't very correct to begin with?

Thanks a lot for any help.

2 Answers

Do it as ASP.NET Core middleware.

In the easiest case with a app.Use(...) method.

public class Startup
{
    public void Configure(IApplicationBuilder app)
    {
        app.Use(async (context, next) =>
        {
            if (!context.Request.IsLocal())
            {
                // Forbidden http status code
                context.Response.StatusCode = 403;
                return;
            }

            await next.Invoke();
        });
    }
}

The delegate calls return on local requests, stopping the middleware pipeline here.

But I'm not 100% certain what you are trying to archive.

Do you want the service only callable from your internal network? The way easier way to do that would be to use docker containers, add the services which need to communicate to each other to the same network and only expose the application to outside the container which really need to communicate with the outside world.

The extension method of IsLocal() should look as the following:

public static class HttpRequestExtensions
{
    public static bool IsLocal(this HttpRequest req)
    {
        var connection = req.HttpContext.Connection;
        if (connection.RemoteIpAddress != null)
        {
            if (connection.LocalIpAddress != null)
            {
                return connection.RemoteIpAddress.Equals(connection.LocalIpAddress);
            } 
            else 
            {
                return IPAddress.IsLoopback(connection.RemoteIpAddress);
            }
        }

        // for in memory TestServer or when dealing with default connection info
        if (connection.RemoteIpAddress == null && connection.LocalIpAddress == null)
        {
            return true;
        }

        return false;
    }
}

To determine whether request is local, we can check if the RemoteIpAddress and LocalIpAddress on the connection are the same, or, in case the local IP is unknown for some reason, if the RemoteIpAddress is a loopback address.

You can read more about it in strathweb article.

Note that it will not work if connection.RemoteIpAddress == "::1". If you do want it to work change it to:

Request.Host.Value.StartsWith(“localhost:”)

Related