Securing Legacy Web-Service site using anonymous authentication for local access

369 views Asked by At

I have a legacy WebForms application that consumes (ASP.net) Web-Services (using anonymous access) that reside on the same IIS server (same domain, different apps each with their own dedicated app pool). This application was hosted in a secured VPN but now there is a request to host it publicly.

What is the expedient (yet secure) method to secure the web services site without doing a substantial re-coding (on the application level). I have tried configuring the Web-Service site in IIS so that it enables only the Service account (Identity) under which the Web Applications App Pool runs but the current request mode coming from the application is always using Anonymous access. I need to restrict access to allow only the this specific web application. I am thinking of an ISAPI filter, but it is not recommended for IIS 7+ ?

ADDENDUM: I would love to find an IIS based solution. Currently I am trying to restrict access based to just the Web Application source. Problem is (as stated) is those request are all "Anonymous" if I could make those requests use "Network Service" or some other local Identity then I would be set.

2

There are 2 answers

0
DaniDev On BEST ANSWER

After more research I have settled on an http Module Solution, as it has the following benefits:

• Minimal Coding

• No need to modify existing code base

• Easy deployment

• Follows existing ASP.Net security models for (Local access)

The Module (VS:DLL Project)

using System;
using System.Web;
using System.Threading;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace FilterModule
{
    class AuthorizeLocal : IHttpModule
    {
        public void Init(HttpApplication app)
        {
            app.BeginRequest += new EventHandler(OnBeginRequest);
        }


        private void OnBeginRequest(Object s, EventArgs e)
        {
            HttpApplication app = s as HttpApplication;
            HttpRequest req = app.Request;
            HttpContext context = app.Context;

            if (!req.IsLocal)    // Is the request from a Local Source?
            {
                context.Response.Close(); // close the response: ends request
            }

            /* Optional Test Code - to view locally create an html page TestModule.html in target site */
            string Identity = Thread.CurrentPrincipal.Identity.Name;
            string filePath = context.Request.FilePath;
            string fileExtension = VirtualPathUtility.GetExtension(filePath);
            string fileName = VirtualPathUtility.GetFileName(filePath);

            if (fileName.ToLower().Equals("testmodule.html"))
            {
                try
                {
                    app.Context.Response.Write("app: " + app.ToString());
                    context.Response.Write("<br/>server: " + app.Server.ToString());
                    context.Response.Write("<br/>Thread.CurrentPrincipal.Identity.Name: " + Thread.CurrentPrincipal.Identity.Name);
                    context.Response.Write("<br/>HttpRequest: " + req.Url.ToString());
                    context.Response.Write("<br/>req.UserHostName: " + req.UserHostName);
                    context.Response.Write("<br/>req.UserHostAddress: " + req.UserHostAddress);
                    context.Response.Write("<br/>filePath: " + filePath);
                    context.Response.Write("<br/>fileName: " + fileName);
                    context.Response.Write("<br/>fileExtension: " + fileExtension);
                    context.Response.Write("<br/>req.IsLocal: " + req.IsLocal.ToString());
                    context.Response.Write("<br/>req.LogonUserIdentity: " + req.LogonUserIdentity);
                    context.Response.Write("<br/>req.UserHostName : " + req.UserHostName);
                    context.Response.Write("<br/>req.AnonymousID " + req.AnonymousID);
                    context.Response.Write("<br/>req.IsAuthenticated : " + req.IsAuthenticated);
                }
                catch (Exception Ex)
                {
                    context.Response.Write("<br/> " + Ex.ToString());
                }
            }

            //if (_eventHandler != null)
            //    _eventHandler(this, null);
        }

        public void Dispose()
        {

        }

    }
}

Implementation

  1. Add the compiled DLL (FilterModule.dll) to the Web Service (site) bin Directory.

  2. Add the following to module definition in the Web Service (or site) configuration file (web.config)

in the <system.webServer> section under <modules>
add the following:

<add name ="FilterModule" type="FilterModule.AuthorizeLocal" />
9
Daniel Botero Correa On

I recommend you to use IdentityServer and OpenIdConnect to do that.

IdentityServer is a .NET/Katana-based framework and hostable component that allows implementing single sign-on and access control for modern web applications and APIs using protocols like OpenID Connect and OAuth2. It supports a wide range of clients like mobile, web, SPAs and desktop applications and is extensible to allow integration in new and existing architectures.

OpenID Connect 1.0 is a simple identity layer on top of the OAuth 2.0 protocol. It allows Clients to verify the identity of the End-User based on the authentication performed by an Authorization Server, as well as to obtain basic profile information about the End-User in an interoperable and REST-like manner. https://connect2id.com/learn/openid-connect

Using this, you can ask identity server to give you an access token and Id Token.

An identity token represents the outcome of an authentication process. It contains at a bare minimum an identifier for the user (called the sub aka subject claim). It can contain additional information about the user and details on how the user authenticated at the OP.

An access token allows access to a resource. Clients request access tokens and forward them to an API. Access tokens contain information about the client and the user (if present). APIs use that information to authorize access to their data.

In your case, you can for example implement the client credentials flow between your Webforms app and the WebService. (In this flow you are not gonna ask nothing to the users of the WebForms app). So, the idea is that the WebForms app is going to ask identity server to give it an access token to access the webservices resources. In the WebService you have to implement the authorization based on whatever you want (Scopes, claims etc). Please read LeastPrivilege blog (Dominick Baier), He is the master of these topic along with his buddy Brock Allen. Since I cannot post more than 1 link in StackOverflow which is really bad, you have to google them or google any additional information.

If you want a user authentication, you can use Implicit, Code or Hybrid flow. But that depends on what you really want to do.

I reckon you may have to do a bit of code but it's not too much. You can figure out a way to ask for authorization before any endpoint is reached.

I hope I was clear. If not, please ask me for more explanation.