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
<httpHandlers> 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 <system.web> element in a
<httpHandlers> 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
<httpHandlers> 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 <httpHandlers> section that looks like this:
<add verb="*"
path="*.cs"
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:
<configuration>
<system.web>
<httpHandlers>
<remove verb="*" path="*.cs" />
</httpHandlers>
...
</configuration>
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:brian.noyes@idesign.net.
Resources