19

I have an ASP.NET MVC website that makes use of WebAPI, SignalR.

I wish for my server (the same server that hosts the website) to make HTTP requests to a WebAPI controller - I wish to do this so that I can hook into my website's SignalR functionality.

I want to make it so that the websites users can't access the methods on the WebAPI controller, but the server can.

I have looked at the options for securing WebAPI requests generally and it seems like I have the following options available to me:

  • Send a username and password over each request AKA Basic Authentication
  • Generate a "Client Certificate" and send that with each request

These are the only two methods that sound like they would work, but I wonder if it's overkill to use these methods if the requests are going to originate from localhost (the same server).

Is it overkill, is there an easier way to restrict HTTP requests from the local machine to a WebAPI controller?

Luke
  • 19,970
  • 26
  • 98
  • 180

2 Answers2

27

If you ONLY wanted to accept requests that originated from the same machine, you could check the IsLocal property of the request context MSDN.

HttpRequest.Context.Request.IsLocal

You could then build it into a custom authorize attribute and register it globally, enforcing the requirement on all of your Web API controllers.

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        // Other Web API configuration code goes here

        // This is a globally registered attribute
        config.Filters.Add(new LocalRequestOnlyAttribute()); 
    }
}

public class LocalRequestOnlyAttribute : AuthorizeAttribute
{
    protected override bool IsAuthorized(HttpActionContext context)
    {
        return context.RequestContext.IsLocal;
    }
}
David L
  • 28,075
  • 7
  • 54
  • 84
  • 1
    Great, I just started researching about `IsLocal` but I'm not quite sure whether it's secure or not. It validates if the IP is `127.0.0.1`, `::1` or the IP address of the current machine. I just wonder if it can be spoofed. – Luke Jun 01 '15 at 12:41
  • There have been questions about exactly that on SO (http://stackoverflow.com/questions/19010217/is-request-islocal-secure-or-can-it-be-spoofed) but I haven't found any answers that suggest it can be spoofed. Even bad headers forwarded from a proxy shouldn't be able to trick it. This answer even goes so far as to call it fully trustable. http://stackoverflow.com/questions/7148821/request-islocal – David L Jun 01 '15 at 12:44
  • Great, I just decomplied it and saw the code. I'll post my findings as an answer also. Thanks! – Luke Jun 01 '15 at 12:59
  • 1
    Glad it helped and interesting decompilation! – David L Jun 01 '15 at 13:08
  • In `.NET 4.7` the `Context` property doesn't exist in `HttpRequest.Context.Request.IsLocal` – Kolob Canyon Jan 09 '19 at 15:27
  • 1
    Great point @KolobCanyon, they changed the API. In 4.7.x, you would check the .IsLocal property that lives directly on the HttpRequest (`HttpRequest.IsLocal`) https://docs.microsoft.com/en-us/dotnet/api/system.web.httprequest.islocal?redirectedfrom=MSDN&view=netframework-4.7.2#System_Web_HttpRequest_IsLocal – David L Jan 09 '19 at 15:41
  • This can also be applied to the API documentation, just in case you don't want it available publicly, except on your server. For that, you will update /Areas/HelpPage/HelpPageConfig.cs instead. – Olanrewaju O. Joseph Apr 29 '20 at 07:40
12

I wanted to clarify as to whether HttpRequest.Context.Request.IsLocal is secure or not.

I just decomplied IsLocal() from HttpWorkerRequest and it reveals the following code:

internal bool IsLocal()
{
    string remoteAddress = this.GetRemoteAddress();
    if (string.IsNullOrEmpty(remoteAddress))
    {
        return false;
    }
    if (remoteAddress == "127.0.0.1" || remoteAddress == "::1")
    {
        return true;
    }
    if (remoteAddress == this.GetLocalAddress())
    {
        return true;
    }
    return false;
}

The first two checks look fine, but I was suspicious and wanted to check to see what this.GetLocalAddress() returns to check against.

In the instance of System.Web.Hosting.IIS7WorkerRequest, this decompiles to the following:

public override string GetLocalAddress()
{
    return this.GetServerVariable("LOCAL_ADDR");
}

In my local environment this returns 127.0.0.1, so all looks good!

Also, according to this post, localhost can't be spoofed.

Community
  • 1
  • 1
Luke
  • 19,970
  • 26
  • 98
  • 180