More Specific Protection
I want authenticated users to be able to browse to a page that lists their purchased software and click a link to download a specific product. Listing products for a user is a no-brainer using the table structure I defined earlier, but what about protecting the link? I've just protected all zip files from anonymous users, but now I want to prevent authenticated users from browsing directly to zip files. For this I need to write a custom handler.
Custom HTTP handlers are classes that implement the IHttpHandler interface. This interface defines a method called ProcessRequest
and a Boolean property called IsReusable
. The property determines whether another request can reuse the same handler so I'm simply going to return a true value here. The method receives an argument of type HttpContext. This variable gives me access to the entire context of the request, including the information in the request and the method to order the request in another direction.
I'm going to create a handler called FileDenialHandler
. This handler simply stops a request in its tracks and redirects the user to a page informing them that access is denied. When this handler gets hit it calls the ProcessRequest
method and performs the simple redirection.
public void ProcessRequest(HttpContext context)
As you can see here, my page resides in the Downloads/Files folder from my root. You can see the entire handler class in Listing 1
Now, obviously the mere existence of this class does not do a thingI have to wire it into the site. To do that I put it in a special section under <system.web>
in the web.config
file lists all special handlers in this configuration section and specifies information about them. This information includes the request verbs that instantiate this handler, the wildcard path that matches files you want this handler to handle, and the type definition for the custom handler. In this case, the addition to this configuration section looks like this:
<add verb="*" path="*.zip"
The "type" argument is standard .NET type-notation with the fully qualified type and component assembly name separated by a comma. If you write your handlers in your actual Web project, you simply leave out the assembly name.
This entry now routes all requests for zip files into my new handler, thus immediately redirecting the requests to my "Access Denied" page. If I had skipped the IIS entry in this example, things would still work the same because my handler is taking care of things. However, the effect I want is for anonymous users to get rerouted to my login page first, before
the system decides that they have accessed an illegal zip file.
If you look at the root web.config
in the framework directory, you'll notice a long list of handlers defined for many common file extensions used in ASP.NET. The handlers defined in the <httpHandlers>
section of this file determine how IIS routes the ASPX pages, ASMX Web services, and all other files appropriately. This list of hander-definitions is also how certain file extensions are disallowedlike *.config
. In fact, files ending with the .config
extension are disallowed using a handler called HttpForbiddenHandler, which automatically displays a "HTTP 403 error Forbidden: Access is denied" page.
You're probably asking why I didn't just use this Microsoft-provided handler for the zip files? The answer: I certainly could have, but I wanted my own "access denied" page. I want to provide my own customized "denial" page that adheres to the look of my Web site. In some cases, I may even want to offer some more information to the user, or maybe even send an "unauthorized attempt" e-mail to an administrator using the code-behind class of this page.
I just blocked all zip files from being downloaded, but is this what I really want to do? Well, yes for now. I want to have absolute control over how files get downloaded from my site. I do not want to allow freeform browsing directly to zip files. Thanks to my table structure, I have a list of projects, users, and a relation list of products purchased by each user. So if I have a user name and a product ID, I can determine if this user purchased this product with a simple database query. And I want my users to click a link that initiates that query and decides whether downloading the file should be allowed. This is where it gets really cool.