Protect Static Files In ASP.NET Web Forms With The Help Of HTTP Handle

First, I will let you know what is the issue I faced in web forms application(ASPX).

I have my legacy web application which was written in asp.net and deployed in IIS 10. We have static files(PDF, JS, CSS, JSON, all types of images(jpg, png,BMP etc..,.)) are located in a sub folder on the site called data, e.g. http://example.com/data/...When an unauthenticated user browse those files

e.g. 

http://example.com/data/image.gif or http://example.com/data/sample.css or http://example.com/data/sample.js),

it will display those files without authentication. In other words, the pages should only be available when the user is authenticated.

IIS’s Default Behavior By default, the IIS web server dispatches requests for ASP.NET resources to the ASP.NET runtime, but handles requests for static content itself. As a result, requests for static content are served without regard to the URL authorization rules spelled out in the ASP.NET application’s configuration.

This is not the behavior we want, as it allows anonymous users to potentially see private content (if they know the direct URL). All it would take is one of the users on the site to unwittingly publish a direct link to the image on his blog or website and the search engines would pick it up and the picture would be revealed to the world at large.

Now My question is: How do I get the ASP.NET engine to handle the requests for the static files in the data folder, so that request for files are authenticated by ASP.NET, and users are not able to deep link to a file and grab files they are not allowed to have?

When I try to browse this issues regarding the same in internet , there are lot of things like enabling runAllManagedModulesForAllRequests="true" but nothings works out. Even though I am running my application in integrated mode in IIS.

Then I came to know about HTTPHandler. Below is the definition of HTTP Handler from Microsoft.

HTTP modules and HTTP handlers are an integral part of the ASP.NET architecture. While a request is being processed, each request is processed by multiple HTTP modules (for example, the authentication module and the session module) and is then processed by a single HTTP handler. After the handler has processed the request, the request flows back through the HTTP modules.

HTTPHandlers are used by ASP.NET web application server to handle specific requests based on extensions. HTTPHandlers run as processes in response to a request made to the ASP.NET website. It is a class that implements the System.

How to implement HTTPHandler to handle any incoming HTTP request with a path like .gif/.js/.css?

  1. Create a class file — it could be FileProtectionHandler.cs
  2. Let FileProtectionHandler class inherit from IhttpHandler Interface.
  3. In FileProtectionHandler class, implement all the methods which are found in IhttpHandler Interface.

When a request comes, it will check whether it will be authenticated or not, if not it will redirect to login page otherwise will send the requested file based on the file extension.

using System;
using System.IO;
using System.Web;
namespace Example.UI {
    public class FileProtectionHandler: IHttpHandler {
        /// <summary>
        /// You will need to configure this handler in the Web.config file of your
        /// web and register it with IIS before being able to use it. For more information
        /// see the following link: https://go.microsoft.com/?linkid=8101007
        /// </summary>
        #region IHttpHandler Members
        public bool IsReusable {
            // Return false in case your Managed Handler cannot be reused for another request.
            // Usually this would be false in case you have some state information preserved per request.
            get {
                return true;
            }
        }
        public void ProcessRequest(HttpContext context) {
            //write your handler implementation here.
            if (!context.User.Identity.IsAuthenticated) {
                context.Response.Redirect("~/Login.aspx");
                return;
            } else {
                string requestedFile = context.Server.MapPath(context.Request.FilePath);
                SendContentTypeAndFile(context, requestedFile);
            }
        }
        private HttpContext SendContentTypeAndFile(HttpContext context, string strFile) {
            context.Response.ContentType = GetContentType(strFile);
            context.Response.TransmitFile(strFile);
            context.Response.End();
            return context;
        }
        private string GetContentType(string fileName) {
            string res = null;
            FileInfo fileInfo = new FileInfo(fileName);
            if (fileInfo.Exists) {
                switch (fileInfo.Extension.Remove(0, 1).ToLower()) {
                    case "pdf": {
                        res = "application/pdf";
                        break;
                    }
                    case "gif": {
                        res = "image/gif";
                        break;
                    }
                }
            }
            return res;
        }
        #endregion
    }
}

The next step is to register the HTTP handler in the Web.config file. Based on your IIS version, it should be added as a child of <system.web> or <system.webServer>. Refer here to register

<httpHandlers>
	<add path="*.gif" verb="*" type="MyProject.FileProtectionHandler" validate="false" />
	<add path="*.png" verb="*" type="MyProject.FileProtectionHandler" validate="false" />
	<add path="*.jpg" verb="*" type="MyProject.FileProtectionHandler" validate="false" />
	<add path="*.js" verb="*" type="MyProject.FileProtectionHandler" validate="false" />
	<add path="*.svg" verb="*" type="MyProject.FileProtectionHandler" validate="false" />
	<add path="*.bmp" verb="*" type="MyProject.FileProtectionHandler" validate="false" />
	<add path="*.json" verb="*" type="MyProject.FileProtectionHandler" validate="false" />
	<add path="*.css" verb="*" type="MyProject.FileProtectionHandler" validate="false" />
</httpHandlers>

That’s all. Now HTTP Handler is ready

Once this configuration information has been specified in Web.config revisit the image file hosted via IIS from your browser. This time while processing the request for the image file IIS allows for the ASP.NET runtime to perform authentication and authorization logic. The ASP.NET runtime notes that the request is coming from an anonymous user and is for a URL that denies anonymous users. Therefore, the anonymous user is redirected to the login page, just like when visiting the image file served by the ASP.NET Development Server.

What are the other things can we do using HTTP Handler?

  1. Dynamic image generation
  2. Create RSS feed and binary data

Conclusion

By default, IIS does not work with the ASP.NET runtime when static content like images, ZIP files, and PDFs are requested. By bypassing the ASP.NET runtime for static content, any URL authorization rules specified in ASP.NET are ignored. As a result, it is possible for unauthorized users to view static content that resides in folders that are protected via ASP.NET’s URL authorization. To protect our static content from unauthorized access, this is one of the way to implement it.

Issues Faced

Blocks CSS/gif/js files used on the login page — To overcome that we need to exclude those pages by verifying the URL in the URL referrer property from the request.

Similar Issue in ASP .NET Core?

No. For asp.net core, we have a built-in middleware hook to support this issue.

References