Bandwidth Leeching Prevention with Http Handlers
 
Published: 18 May 2007
Abstract
In this article, Joseph explains how important it is to implement Http handlers in web applications to prevent bandwidth leeching.
by Joseph Chahine
Feedback
Average Rating: 
Views (Total / Last 10 Days): 21427/ 48

Introduction

Unfortunately, a web server is exposed to many attacks targeting its bandwidth leading many times to a denial of service state on the server.

Bandwidth leeching, also called hotlinking, is described by the practice of hosting on a web server, web pages that contain unauthorized content links, known as hotlinks, direct links, or remote links, to files hosted by another site on another web server, in order to consume the bandwidth of the latter server without having its pages viewed.

A usual case is when multiple pages (frequently hosted under multiple websites to cause more damage) on the internet have links to a website's images, cascading stylesheets, JavaScript files, etc... Whenever these pages are visited, the uplink bandwidth of the targeted web server is consumed. The more the external links to the targeted website's contents are, the more its bandwidth is consumed. At a certain point, the bandwidth won't be enough to serve new connections, it has been leeched!

Solution

Stopping bandwidth leeching may seem to be a daunting task, but you will see that it is not. All you need to do is to find a way to identify where the requests to your website's contents are coming from. If the Http Referrer isn't the website's domain address itself (or one of its addresses), then the request comes from an external website and it should be discarded. To monitor Http requests, you need to write your own Http Handler(s) to handle requests to images and other file types that must not be accessed by any external website.

As the "System.Web.UI.PageHandlerFactory" handler handles .aspx files, you will see in this article a handler that handles .jpg, .jpeg, .gif, .css, and .js files. You can write as many Http handlers as you want for your web application. The same Http handler can be used for multiple applications.

Explanation

Let's examine how Microsoft's most popular web server parses Http requests. When Internet Information Services (IIS) receives an Http request, it parses the extension of the file targeted by the URL. Usually, file extensions are associated with assemblies containing methods and functions that will process requests. Mappings from file extensions to assemblies are saved in the IIS metabase. When ASP.NET is installed, it adds entries to the metabase associating some file extensions including .aspx and .asmx with the "aspnet_isapi.dll" assembly file.

The "HTTP_REFERER" server variable can tell when a request is made from another website. If someone puts the URL of an image, for example, into a web browser, or follows a direct link from an email, there won't be a referrer.

In .NET, an Http Handler class should implement the "IHttpHandler" interface as per the following code listing.

To go in tandem with this article's contents, it is recommended that you try to implement and test an Http Handler yourself. It's an easy task. Start by opening Visual Studio .NET. Create a Class Library project and name it "HttpHandlersLibrary." Delete the "Class1.vb" (or "Class1.cs") file created by default and create a class called "FileTypeHandler.vb" (or "FileTypeHandler.cs"). Replace the contents of the already created class by the contents of the following listing and rebuild the project. In the bin folder, the "HttpHandlersLibrary.dll" assembly file will be created.

 

Listing 1 - VB Code

Imports System
Imports System.Web
 
Public Class FileTypeHandler
    Implements IHttpHandler
 
    '' The ProcessRequest method decides what to do with the request.
    Public Sub ProcessRequest(ByVal Context As System.Web.HttpContext)_
 Implements System.Web.IHttpHandler.ProcessRequest
        If Context.Request.ServerVariables("HTTP_REFERER"Is Nothing Then Exit Sub
 
        '' strFileName holds the full path of the request, 
''for example http://domain/folder/page.aspx
        Dim strFileName As String = Context.Server.MapPath(Context.Request.FilePath)
 
        Dim cfg As New System.Configuration.AppSettingsReader
 
        '' The strDomainNameCollection is an array of comma-separated
        '' values holding the address(es) of your website.
        Dim strDomainNameCollection As String() = CType(cfg.GetValue("DomainName", _
GetType(System.String)), String).ToLower.Split(",")
 
        '' Now we check if the Http Referrer is listed in strDomainNameCollection.
        If Array.IndexOf(strDomainNameCollection, _
 Context.Request.ServerVariables("HTTP_REFERER").ToLower.Split("/")(2)) > -1 Then
            Select Case strFileName.Split(".")(strFileName.Split(".").Length - 1).ToLower
                Case "jpg""jpeg"
                    Context.Response.ContentType = "image/JPEG"
                    Context.Response.WriteFile(strFileName)
                Case "gif"
                    Context.Response.ContentType = "image/GIF"
                    Context.Response.WriteFile(strFileName)
                Case "js"
                    Context.Response.ContentType = "text/javascript"
                    Context.Response.WriteFile(strFileName)
                Case "css"
                    Context.Response.ContentType = "text/css"
                    Context.Response.WriteFile(strFileName)
                Case Else
            End Select
        End If
    End Sub
 
 
    '' This property tells the server whether or not the handler object can be reused.
    Public ReadOnly Property IsReusable() As Boolean _
 Implements System.Web.IHttpHandler.IsReusable
        Get
            Return True
        End Get
    End Property
 
End Class

Listing 2 - C# Code

using System;
using System.Web;
 
namespace HttpHandlersLibrary
{
  public class FileTypeHandler: IHttpHandler
  {
    /// The ProcessRequest method decides what to do with the request.  
    public void ProcessRequest(System.Web.HttpContext Context)
    {
      if (Context.Request.ServerVariables["HTTP_REFERER"] == null)
      {
        return ;
      }
 
      /*strFileName holds the full path of the request, for example
       http://domain/folder/page.aspx*/
      string strFileName = Context.Server.MapPath(Context.Request.FilePath);
 
      System.Configuration.AppSettingsReader cfg = new
        System.Configuration.AppSettingsReader();
 
       /// The strDomainNameCollection is an array of comma-separated
       /// values holding the address(es) of your website.
      string[]strDomainNameCollection = System.Convert.ToString(cfg.GetValue(
        "DomainName", typeof(System.String))).ToLower().Split(Convert.ToChar(
        ","));
 
      /// Now we check if the Http Referrer is listed in strDomainNameCollection.
      if (Array.IndexOf(strDomainNameCollection,
        Context.Request.ServerVariables["HTTP_REFERER"].ToLower().Split
        (Convert.ToChar("/"))[2]) >  - 1)
      {
        switch (strFileName.Split(Convert.ToChar("."))[strFileName.Split
          (Convert.ToChar(".")).Length - 1].ToLower())
        {
          case "jpg":
            Context.Response.ContentType = "image/JPEG";
            Context.Response.WriteFile(strFileName);
            break;
          case "jpeg":
            Context.Response.ContentType = "image/JPEG";
            Context.Response.WriteFile(strFileName);
            break;
          case "gif":
            Context.Response.ContentType = "image/GIF";
            Context.Response.WriteFile(strFileName);
            break;
          case "js":
            Context.Response.ContentType = "text/javascript";
            Context.Response.WriteFile(strFileName);
            break;
          case "css":
            Context.Response.ContentType = "text/css";
            Context.Response.WriteFile(strFileName);
            break;
          default:
            break;
        }
      }
    }
 
/* This property tells the server whether or not the handler object can be reused.*/
    public bool IsReusable
    {
      get
      {
        return true;
      }
    }
 
  }
}

Now use Visual Studio .NET to create an ASP.NET Web Application project in the following location http://localhost/TestingHttpHandlers. Delete the "WebForm1.aspx" file and create a Web Page called "Default.aspx." Drag and drop into this page a .jpg or .jpeg or .gif picture that you've already included in the solution.

Now you need to map the file extensions to the new Http Handler(s) assembly(ies) in IIS. For this purpose, go to the Internet Information Services manager console (you can do this by typing in your Run command "inetmgr.msc" without the quotes), right click the already created website (or virtual directory), select the "Home Directory" (or "Virtual Directory") tab, click the "Configuration" button (see Figure 1) and select the "Mappings" tab in the pop-up, click the "Add" button (see Figure 2) and browse to select the "HttpHandlersLibrary.dll" file previously created, input ".jpg" (without the quotes) in the "Extension" field and click the "OK" button (see Figure 3). Follow the same way to add mappings for .jpeg, .gif, .css, and .js extensions.

Figure 1

 

Figure 2

 

Figure 3

 

One more thing to do is to add the "HttpHandlers" entry in the Web.config file. This informs the .NET Framework about the mappings between handlers and file extensions. The same way we did above to map file extensions to assemblies in IIS, we need to map file extensions to handler classes since the "HttpHandlersLibrary.dll" assembly can hold multiple classes implementing the IHttpHandler interface.

Listing 3

<configuration>
    <system.web>
        <HttpHandlers>
            <add verb="*"
 path="*.jpg" type="HttpHandlersLibrary.FileTypeHandler, HttpHandlersLibrary"/>
            <add verb="*"
 path="*.jpeg" type="HttpHandlersLibrary.FileTypeHandler, HttpHandlersLibrary"/>
            <add verb="*"
 path="*.gif" type="HttpHandlersLibrary.FileTypeHandler, HttpHandlersLibrary"/>
            <add verb="*"
 path="*.js" type="HttpHandlersLibrary.FileTypeHandler, HttpHandlersLibrary"/>
            <add verb="*"
 path="*.css" type="HttpHandlersLibrary.FileTypeHandler, HttpHandlersLibrary"/>
        </HttpHandlers>
    </system.web>
 
    <appSettings>
        <add key="DomainName"
 value="127.0.0.1,localhost"/>
    </appSettings>
</configuration>

If you're using Visual Studio .NET 2003 make sure to write "httpHandlers" with a lower case starting "h" character.

You need to add for each file extension a sub-entry in the "HttpHandlers" section. The path contains the extensions each preceded by a wildcard. The type has the following format "NamespaceName.ClassName, AssemblyName." Make sure to respect the casing in "Web.config" since it's a case-sensitive file.

Note the "appSettings" section of the "Web.config" file, I have included in it the domain address(es) considered as trusted.

Now it's time to test what you've done so far.

Run the web application locally, you'll see that the image will show up in the page. If you now try to access the image directly through http://localhost/TestingHttpHandlers/ImageName.jpg, you'll get a blank page since the "HTTP_REFERER" server variable is not defined. Same thing will happen if you try to directly access this image from another computer since the "HTTP_REFERER" will hold the address of the latter computer which is not listing in the "DomainName" key in the "appSettings" section.

Conclusion

By using Http Handlers, you can skip the ASP.NET default page-processing pipeline and have a full control over how the framework servers up requested data for different purposes, preventing bandwidth leeching being one of them.



User Comments

No comments posted yet.

Product Spotlight
Product Spotlight 





Community Advice: ASP | SQL | XML | Regular Expressions | Windows


©Copyright 1998-2021 ASPAlliance.com  |  Page Processed at 2021-02-27 11:17:59 PM  AspAlliance Recent Articles RSS Feed
About ASPAlliance | Newsgroups | Advertise | Authors | Email Lists | Feedback | Link To Us | Privacy | Search