Secure Resource and Document Files

Configure IIS and ASP.NET to protect resource files with the same access control constraints as the rest of your app.

asp:feature

LANGUAGES: All .NET Languages

ASP.NET VERSIONS: 1.0 | 1.1

 

Secure Resource and Document Files

Configure IIS and ASP.NET to protect resource files with the same access control constraints as the rest of your app.

 

By Brian Noyes

 

A common question when developing ASP.NET applications is, "How do you secure files such as images, documents, audio files, XML files, and other resources that are part of your Web app?" The answer depends on what kind of security you are using for the rest of your ASP.NET application. Understanding the solutions requires a little understanding of the steps an HTTP request goes through when IIS and ASP.NET service it. For brevity, I am going to refer to these files as resource files. By this I mean any kind of file (image, audio, video, document, PDF file, source, and so on) that is not part of the pages that your application URLs usually address, but does reside in your Web application's folders.

 

The processing chain that occurs from when a request comes into the server until when it goes back out to the client as a response is often called the HTTP pipeline. I am assuming you are using IIS for this explanation. Although ASP.NET could be configured to work with other Web servers, the vast majority of deployed applications use IIS, so that is all I will focus on here.

 

Peer Down the HTTP Pipeline

The first thing that occurs when an HTTP request is received is that IIS must decide what to do with it. It does this by looking at the URL and checking the file extension at the end of it. Based on this file extension, IIS will decide whether it knows how to service requests for that kind of file, or whether it should pass it off to one of the ISAPI extensions configured in IIS. For example, if a request for an ASP file comes in, IIS will see that it has an ISAPI extension (asp.dll) registered to handle requests for files of that type. It then hands the request off to that extension and is out of the loop until the extension returns the response. IIS knows how to service requests for common Web files, such as HTML and image files, so if no ISAPI extension is registered to handle requests for those types of files, IIS will simply return the file as a stream.

 

So how does ASP.NET get in the game? Well, ASP.NET has an ISAPI extension registered in IIS as well, aspnet_isapi.dll, and it is that extension that is responsible for handing off requests for ASP.NET pages, Web services, and resources to the ASP.NET worker process. When you install .NET, the installer registers the ISAPI extension and associates a bunch of known .NET file types (such as ASPX, ASCX, CS, VB, CONFIG, and so on) with the ASP.NET ISAPI extension in IIS so that requests for those file types will get passed off to ASP.NET. From there the ASP.NET worker process figures out what to do with the request based on which HTTP handlers are registered for that file type in .NET, passing the request through any configured HTTP modules on the way (see Figure 1).

 


Figure 1. The HTTP request processing pipeline in .NET takes any requests routed to ASP.NET from IIS and hands them to the appropriate HTTP handler endpoint based on the sections in the web.config and machine.config files.

 

The built-in registrations for ASP.NET are buried in your machine.config file under the element in a element. There are a bunch of handlers that ship with the .NET Framework, and it is these handlers that act as endpoints for requests to enable a lot of the processing, such as the processing of ASPX pages and Web services that most people just think of as part of the ASP.NET runtime. You can override and supplement the handlers configured in machine.config by adding an section in your application web.config file.

 

For a little more information on the HTTP pipeline and what HTTP modules do in that processing chain, see my article Extend ASP.NET With HTTP Modules.

 

The next thing to understand is how to secure an ASP.NET application in general. That is a whole topic unto itself, so I will have to assume you are familiar with the Windows, Forms, and Passport authentication options for securing an ASP.NET application. The steps discussed in the rest of this article focus mainly on either Forms or Passport authentication. Whether they apply to Windows authentication or not depends on whether the files you are interested in protecting are mapped to ASP.NET in IIS.

 

If you secure an ASP.NET application with Windows authentication and a request is made for a file that resides within your application that is not mapped to ASP.NET (such as a bitmap, JPEG, WAV, or XML file), IIS decides whether to serve that file up based on the Access Control Lists (ACLs) for the file and the authenticated identity of the logged on user. The files will be automatically protected at the same level as the rest of your application unless you specifically relax the ACLs. If you map the files you want to protect to ASP.NET with Windows authentication, IIS will step out of the way and leave it up to ASP.NET to do the access checks. However, those checks will still be based on the ACLs and the logged on user if you are using Windows authentication.

 

If you are using Forms or Passport authentication, files not mapped to ASP.NET can be served up to even non-authenticated users, even when they reside in the folders of your ASP.NET secured application. So if you want to protect access to resource files that are part of your application when using Forms or Passport authentication, the first step is to get them mapped into ASP.NET.

 

It's All About Configuration

Once you understand the processing chain and the way IIS makes decisions about what ISAPI extension is responsible for serving a request for a file, the steps to securing resource files become more clear. The first thing is to get the file extensions for the files you want to protect mapped to ASP.NET. To do this, you bring up the IIS Management Console and select the virtual directory or root for your Web application. You then select Properties, and in the Directory tab, press the Configuration button toward the bottom. That takes you to the Application Configuration dialog, which is where the mappings I keep talking about are configured in IIS (see Figure 2).

 


Figure 2. IIS decides where to send requests based on mappings between file extensions and ISAPI extensions in the Application Configuration settings.

 

The easiest way to add a file extension that is mapped to ASP.NET, such as ASCX, is to select Edit for one of the file extensions that is already mapped to ASP.NET, and copy the path to the ISAPI extension executable using Ctr-Ins (Ctrl-C/Ctrl-V doesn't work in this dialog for some reason). Then cancel out of editing that one and add a new one for the file extension you want to map by pasting the executable path in with Shift-Ins and entering the file extension (for example, BMP), and clicking on OK (see Figure 3).

 


Figure 3. To add a mapping for a file extension to ASP.NET, you add an Application Extension Mapping from the Application Configuration dialog.

 

Once you have done that, all subsequent requests for URLs ending in that file extension for that Web application will pass the request to ASP.NET for handling. If the ASP.NET application is configured for authentication, then any access to files of that type will be authenticated by ASP.NET, just like it's done in the rest of the application.

 

There is one other scenario you may need to accommodate. What if the kind of file you want to expose is already mapped to ASP.NET, and you want to expose it in your authenticated ASP.NET app, but it is being blocked by the machine.config file or web.config file at the site level because there is a handler that denies access to the file? For example, by default, source code files are protected and cannot be viewed through the browser, even by authenticated users. It works like this because there is an entry in the machine.config file section that looks like this:

 

 type="System.Web.HttpForbiddenHandler"/>

 

The HttpForbiddenHandler blocks access to any files it is mapped to by the specified path in the config file entry. Your system administrator very well may have added entries like that at a machine level to prohibit access to XML files, documents, or any number of other file types. However, it may actually make sense to expose those file types in your app, depending on its purpose. If that is the case, you can override the handlers from machine.config by either removing or replacing them at the application level. So, for example, if you were building a site that allowed authenticated users to view source code files for development purposes, you can remove that forbidden handler at the application level by adding a section such as this one to your web.config:

 

    

  

    

      

    

   ...

 

Also remember that you can enforce role-based security on specific files and folders using ASP.NET, so you might want to add a location-based authorization tag to allow only people in a developer role to view these files. See the docs on configuring authorization at the file level for more info on that (see the Resources section at the end of this article).

 

Don't Forget About Performance

Just because you can do something doesn't always mean you should do it. Although it is simple to configure IIS and ASP.NET so that resource files are protected under the same authentication and authorization constraints as the rest of your app, you shouldn't just blindly do this. Do it only if it is really necessary. There is a minor performance hit for the extra steps of passing the request for a resource off to ASP.NET, having ASP.NET check to see if access is granted, and then having ASP.NET to return the resource to IIS. If you have a page composed of hundreds of tiny images that comprise the borders and graphics layout for your site, ASP.NET is going to have to do an awful lot of work to protect and serve the images that you really don't need protection anyway. On the other hand, if you have a subscription-based image library, you definitely won't want those images accessible to unauthenticated users. So just be smart about applying this concept. You might want to serve up the images you don't care about protecting from a separate virtual directory that does not have image files mapped to ASP.NET. Then you would put only those files you really need to protect within your application folders, and configure things as I've discussed in this article.

 

Brian Noyes is a software architect with IDesign, Inc. (http://www.idesign.net), a .NET-focused architecture and design consulting firm. Brian specializes in designing and building data-driven distributed Windows and Web applications. He has more than 12 years of experience in programming, engineering, and project management, and is a contributing editor and writer for C#PRO, asp.netPRO, and other publications. Contact him at mailto:[email protected].

 

Resources

 

 

 

Hide comments

Comments

  • Allowed HTML tags: <em> <strong> <blockquote> <br> <p>

Plain text

  • No HTML tags allowed.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Lines and paragraphs break automatically.
Publish