Login | Register   
LinkedIn
Google+
Twitter
RSS Feed
Download our iPhone app
TODAY'S HEADLINES  |   ARTICLE ARCHIVE  |   FORUMS  |   TIP BANK
Browse DevX
Sign up for e-mail newsletters from DevX


advertisement
 

Protect Your Downloadable Files Using HTTP Handlers : Page 4

This article attacks a problem for which I have heard many solutions: How can you offer file downloads on the Internet and protect them from unauthorized downloading?


advertisement
Controlling the Download
I just taught you how to write a handler and install it so that it's used for certain file requests; although my handler didn't do much except reroute the request somewhere else. ASP.NET gives you another file extension that you can use for handlers right out of the box, without the need to install them in the web.config file—ASHX. You can create a class that ends with this extension, implement the IHttpHandler interface, and browse directly to that class, much like if it were a page. In fact it's very similar to a page, except for the fact that you don't get a Web Form and a Code-Behind class, so it's a much cleaner solution.

Now, I want to create a new handler which I will call Download.ashx and have users browse to that location, specifying some information in the QueryString arguments. This kind of URL is what my download links contain.

~/Downloads/Download.ashx?Product=101

This URL signifies a request to download the file associated with product 101. Now it's up to the handler code to determine how to act.

A user or a link can access the above URL in an attempt to obtain a file. In doing so, the handler's ProcessRequest method executes, and here is where I put my checks.

I've authenticated my users using standard Forms Authentication, which gives me access to the User object in my site context. Remember the HTTP context is passed into the handler's ProcessRequest method so I have access to everything I need right there. The User object allows me to obtain the name of the authenticated user using User.Identity.Name. Remember that I use this method to access the user in my Users table. Before accessing the user's name, my code checks to see if they've authenticated using the User.Identity.IsAuthenticated Boolean value, and if not, it sends them to the "access denied" page. Also, I can access the product number sent into the request like this:

context.Request.QueryString["Product"]

So now I have the product number and the user name; I'm on my way. I now have the two pieces of information I need to access my UserProducts table and determine if this user purchased this product. Not only that, but remember I store the product's file name in this table too—starting to get the picture?

OK, I have the user name and the product number, and I've used them both to determine if the user has purchased this product. If the answer is No, I can perform the same kind of redirection I did in the previous handler and shoot them over to an "access denied" page. For a cleaner solution, I may choose to redirect them to a page informing them that they have not purchased this product and offer them the ability to do so—gotta love Web-oriented capitalism.

If I've determined that the user has purchased this product, I can determine what the file is by looking at my ProductFileName field as I specified earlier. In my own design, I don't store the entire path here, just the file name. I can obtain the file folder from a setting in my web.config, but you can do this however you want. So I now have the entire file path and name and the authority to begin a download. I'm going to do this in a method called StartDownload just for better encapsulation:



private void StartDownload( HttpContext context, string downloadFile) { context.Response.Buffer = true; context.Response.Clear(); context.Response.AddHeader( "content-disposition", "attachment; filename=" + downloadFile); context.Response.ContentType = "application/zip"; context.Response.WriteFile( "~/Downloads/Files/" + downloadFile); }

My ProcessRequest method calls the StartDownload method. You can see the entire listing for Download.ashx in Listing 2, but let's go over the code shown above.

My method receives the name of the file as well as the HttpContext. From here, all I'm doing is clearing out the response buffer, essentially eliminating anything from the request that got me here in the first place, setting up a new header, and then setting up the content type. The content type is standard "content type" notation that you see in many constructs today. Finally, I'm just spitting out the file (prefaced with the path) using the WriteFile method. The end result is the user receiving an "Open or Save As" window.

Notice that I'm using WriteFile to stream out the zip file, as opposed to redirecting the user to the zip file using Response.Redirect. If I use the latter method, the FileDenialHandler kicks in and sends the user to the "access denied" page instead.

Using this technique, clever users can browse directly to the Download.ashx file but they're not going to get around the security checks. And if they try to browse directly to the zip file, they get redirected to the "access denied" page by the FileDenialHandler handler.

Pretty simple when you think about it.

Note the differences in the two types of handlers. One is a standard C# (or VB.NET) class that you can place in an external component. This is great if you need to write reusable handlers because you can compile them all into one DLL and share them among your sites. Of course, you need to register these in the web.config as I showed you earlier. The other type is an ASHX and you add it to your Web site just like one of its ASPX pages. In fact, you can probably accomplish something very similar to what I'm doing with Download.ashx with an ASPX page, but the ASHX handlers are a much cleaner solution and not subject to the page life cycle a standard ASPX page goes through.

Other Uses for Handlers
Custom HTTP handlers have many other uses too. In my site, I'm using a pretty slick method of syndicating the contents of some of my business collections and I do that by browsing to an "rss" location. I registered the "rss" extension in IIS so it gets routed through ASP.NET. Then I created a custom handler to intercept all rss URLs and figure out what XML to stream out. Hey! that would make another pretty cool article!

Another use includes intercepting all image files and embedding a watermark in them. The possibilities are endless, so use your imagination.

Of all the approaches used to secure file downloading in ASP.NET, using HTTP handlers is the most effective and the most elegant. Using it with the combination of techniques I've shown you here not only ensures tight security from unauthorized users, but also grants you absolute control over how things happen when a user attempts a file download. Now, remember that once the file is in the possession of the user, there's nothing stopping them from giving it out to someone else, so you must have some kind of licensing and registration built into your product—but that's the topic for another article.



Miguel A. Castro is President of InfoTek Consulting Group, Inc., a professional consulting firm that specializes in architecting, designing, and developing solutions using Microsoft .NET technologies. His VB background goes all the way back to 1.0. Miguel's focus for the last couple of years has been the .NET Framework and languages; he is fluent in both VB .NET and C#. His programming experience goes back 20 years when he started on TRS-80s, Apple IIs, and Atari 800 computers. Miguel lives in Lincoln Park, New Jersey with his wife Elena and his daughter Victoria.
Comment and Contribute

 

 

 

 

 


(Maximum characters: 1200). You have 1200 characters left.

 

 

Sitemap
Thanks for your registration, follow us on our social networks to keep up-to-date