Use an HttpHandler to stop bandwidth leeching of your images
posted by
Jeff
HttpHandlers make it easy to handle certain file types with a simple interface that anyone can write code for.
Few things are more annoying than checking your server logs to find that an image is being served hundreds, maybe thousands of times a day to other sites. It's called bandwidth-leeching, and aside from the obvious copyright issues, it eats the bandwidth that you're paying good money for. Sure, bandwidth is getting cheaper, but you're not a free hosting service.
Fortunately, ASP.NET makes it very easy to perform some checks on image requests before the image is served through the use of an HttpHandler. You may not realize it, but your server is already handing off requests to HttpHandlers. For example, .aspx files are
being handled by the System.Web.UI.PageHandlerFactory. Your .cs or .vb files are
being handled by System.Web.HttpForbiddenHandler to generate an error for
snooping users. It stands to reason then that you can write your own code to
handle .jpg images.
To make this work, you'll first have to set up IIS to handle requests for
.jpg files. In your site's properties, click the "home directory tab" and click
the "configuration" button. You'll get a form like this:

Find the entry for .aspx, click edit, and copy the path in the executable
field. This is the aspnet_isapi.dll for the current version of the framework
that is responsible for the plumbing between IIS and the ASP.NET engine. Cancel
out of that dialog and click "add." Paste the path into the executable,
use the extension .jpg and set
your verbs limited to "GET, POST, HEAD" like this:

Now any request for a .jpg file on the site will be handled by ASP.NET.
Since the server-wide machine.config file doesn't specify what class should
handle the request, a default handler is used unless we add a few lines to our
web.config file. We'll get to that in a moment.
Let's write our HttpHandler. HttpHandlers must implement the IHttpHandler
interface. We're lucky, because this interface only requires two members, the
ProcessRequest(HttpContext) method and the Boolean property IsReusable.
ProcessRequest(HttpContext) decides what to do with the request. Understand that
there is no automatic plumbing that will retrieve a file for any request that is
to be processed by an HttpHandler, so we'll have to write some. IsReusable does
exactly what it sounds like it should, it tells the server whether or not the
handler object can be reused.
Here's the code:
C#
using System;
using System.Web;
public class JpgHandler : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
string FileName = context.Server.MapPath(context.Request.FilePath);
if (context.Request.ServerVariables["HTTP_REFERER"] == null)
{
context.Response.ContentType = "image/JPEG";
context.Response.WriteFile("/no.jpg");
}
else
{
if (context.Request.ServerVariables["HTTP_REFERER"].IndexOf("mydomain.com") > 0)
{
context.Response.ContentType = "image/JPEG";
context.Response.WriteFile(FileName);
}
else
{
context.Response.ContentType = "image/JPEG";
context.Response.WriteFile("/no.jpg");
}
}
}
public bool IsReusable
{
get
{ return true; }
}
}
VB .Net
Imports System
Imports System.Web
Public Class JpgHandler
Implements IHttpHandler
Public Sub ProcessRequest(context As HttpContext)
Dim FileName As String = context.Server.MapPath(context.Request.FilePath)
If context.Request.ServerVariables("HTTP_REFERER") Is Nothing Then
context.Response.ContentType = "image/JPEG"
context.Response.WriteFile("/no.jpg")
Else
If context.Request.ServerVariables("HTTP_REFERER").IndexOf("mydomain.com") > 0 Then
context.Response.ContentType = "image/JPEG"
context.Response.WriteFile(FileName)
Else
context.Response.ContentType = "image/JPEG"
context.Response.WriteFile("/no.jpg")
End If
End If
End Sub
Public ReadOnly Property IsReusable() As Boolean
Get
Return True
End Get
End Property
End Class
The first thing we do in the ProcessRequest(HttpContext) method is get the
name of the image that is being requested. The context object gives us a
reference to all of the things we're used to seeing in our normal page code, in
this case we want to know what file is being requested and wrap it in a MapPath
call to get its position on the disk.
Next we check to see if there's a value in the request's HTTP referrer value.
I like to use this because I don't want people seeing my images unless it's in
the context of a page on my site (where they can see the ad and I can get paid
for the impression). If someone puts the URL of the image into a new browser or
follows a link from an e-mail, there won't be a referrer. If that's the case, I
set the content type to "image/JPEG" and serve them an image called "no.jpg,"
which has a friendly message in it saying they can't see what they're looking
for.
One side note about this. Some so-called "security" software strips out the
referrer from every request, which is really paranoid and stupid. People using
this software will only see the leech notice image.
Assuming there is a referrer, the next thing to check is if it's a page on
our site. Our analysis of the referrer string isn't complex, we just want to
make sure that there's some instance of our domain name in it. The IndexOf()
method of the string object works perfectly. If our domain is there, we serve
the image they were looking for. If not, the alternate image is all they'll see.
You'll need to compile this class into an assembly that you'll put in the
application's /bin folder. If you're using Visual Studio or one of the other
IDE's, this will be taken care of for you. If not, you'll have to use the
command line compiler in a command window. (Search the .Net SDK or Google to
find out how to use the .Net command-line compilers.)
There's one more step to make this work. Your web.config file has to tell the
server how to handle the .jpg requests. Drilling down through the file's schema,
you'll need to add the httpHandlers section if it's not already there, then
place an add tag.
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.web>
<httpHandlers>
<add verb="*" path="*.jpg" type="JpgHandler, MyDll"/>
</httpHandlers>
</system.web>
</configuration>
The type is the name of the class, JpgHandler, followed by the assembly in
your /bin folder that contains the class.
That's all it takes! Now bandwidth leechers will only see a nasty message and
maybe provide free advertising for you on someone else's site.
©2010, POP World Media, LLC. All rights reserved
Legal, privacy, terms of service