Introduction
What the article explains
The article explains how an HTTP module can be used to combat leech requests to resources on your domain through GET or POST HTTP request methods. The code snippet provided defines a class that implements the IHttpModule
interface. An instance of this class will hook into the ASP.NET runtime and intercept the BeginRequest
event of the HttpApplication
object and determine if the requested URI came from a leech link on another website. Leech links are simply links pointing to resources on your site without your knowledge or authorization.
This technique can be used to prevent the outside world from posting links to resources on your domain without authorization to do the same. For example, if you maintain a list of sites that have been authorized to provide links to your website, then you can put into effect a selective screening routine. On the other hand, if there are domains that you do not want to be referred from, this technique will again, prove equally useful. I have not provided the code to these ends though. My intent here is to simply illustrate a basic possibility. But you could mail me if you are interested to know how to go about it, but I believe you should be able to pull it off without much ado after reading through this article.
Background
ASP.NET Pipeline
The ASP.NET pipeline represents a series of extensible objects that work in a sequential-chain and act on an incoming request, one after the other. As the requests pass through the pipeline, they are modified and filtered until they are finally handed over to a handler object that emits a suitable response back to the client. Through the use of these modules and handlers, we can effectively extend the capabilities our web server, just like what ISAPI extensions and filters are used to do for IIS.
HttpModules
An HTTP module is an assembly that handles events raised by an HttpApplication
object. ASP.NET includes a set of HTTP modules that can be used by your application. For example, the SessionStateModule
is provided by ASP.NET to supply session-state services to an application. There are a number of other system-level HTTP modules, providing services ranging from authentication to state management to output caching. Here is the list of modules registered on my machine's machine.config file: -
<httpModules>
<add name="OutputCache"
type="System.Web.Caching.OutputCacheModule"/>
<add name="Session"
type="System.Web.SessionState.SessionStateModule"/>
<add name="WindowsAuthentication"
type="System.Web.Security.WindowsAuthenticationModule"/>
<add name="FormsAuthentication"
type="System.Web.Security.FormsAuthenticationModule"/>
<add name="PassportAuthentication"
type="System.Web.Security.PassportAuthenticationModule"/>
<add name="UrlAuthorization"
type="System.Web.Security.UrlAuthorizationModule"/>
<add name="FileAuthorization"
type="System.Web.Security.FileAuthorizationModule"/>
</httpModules>
The number of modules that get to intercept the request is based upon these settings within the host machine's machine.config file and the application's web.config file. Note that, HTTP modules are typically executed for every request irrespective of the file type.
When an HTTP module is hooked into the pipeline (via an entry in web.config), the ASP.NET runtime calls the module's Init
and Dispose
methods. Init
is called when the module attaches itself to the HttpApplication
object and Dispose
is called when the module is detached from HttpApplication
. The Init
and Dispose
methods represent the module's opportunity to hook into a variety of events exposed by the HttpApplication
object. These events include the beginning of a request, the end of a request, a request for authentication, and so forth. The Init
method takes the HttpApplication
object and maps event handlers to the desired events.
To make a class act as a module, it should implement the member signatures of the IHttpModule
interface. The members are as follows: -
Sub Init(ByVal Application As HttpApplication)
Sub Dispose()
HttpApplication
The HttpApplication
object raises several events that reflect different processing stages for a request. Any HttpModule
class registered with the HttpApplication
object to receive notification can consume these events. As and when the HttpApplication
object raises an event, the same event is passed to all the modules registered to receive notification of the event. The ordinal of a module in the chain is determined by its order in the module listing within the web applications configuration file (machine.config and web.config).
The events that are raised by the HttpApplication
object are as follows (as listed in MSDN): -
AcquireRequestState
- When ASP.NET acquires the current state (for example, session state) associated with the current request.
AuthenticateRequest
- When a security module has established the identity of the user.
AuthorizeRequest
- When a security module has verified user authorization.
BeginRequest
- When the first event in the HTTP pipeline chain of execution responds to a request.
Disposed
- When ASP.NET completes the chain of execution when responding to a request.
EndRequest
- When the last event in the HTTP pipeline chain of execution responds to a request.
Error
- When an unhandled exception is thrown.
PostRequestHandlerExecute
- When the ASP.NET handler (page, XML Web Service) finishes execution.
PreRequestHandlerExecute
- Just before ASP.NET begins executing a handler such as a page or XML Web Service.
PreSendRequestContent
- Just before ASP.NET sends content to the client.
PreSendRequestHeaders
- Just before ASP.NET sends HTTP headers to the client.
ReleaseRequestState
- After ASP.NET finishes executing all request handlers; also causes state modules to save the current state data.
ResolveRequestCache
- When ASP.NET completes an authorization event to let the caching modules serve requests from the cache, bypassing execution of the handler (the page or XML Web Service, for example).
UpdateRequestCache
- When ASP.NET finishes executing a handler in order to let caching modules store responses that will be used to serve subsequent requests from the cache.
Using the code
RefererFilter.vb
This is our HTTP module class. It implements the IHttpModule
interface. The Init
method is where you inform the global HttpApplication
object about our interest in being notified about its events. In this case, we express a desire to handle its BeginRequest
event with the following statement: -
AddHandler Application.BeginRequest, AddressOf Me.Application_BeginRequest
The AddHandler
method passes a pointer to our filter method (Application_BeginRequest
) as a delegate to the Application
object. The pointer to the filter method is obtained by using the AddressOf
operator.
When the BeginRequest
event is raised by the HttpApplication
object, the latter will invoke the Application_BeginRequest
method of our class. All this method does is to check if the referral URI is part of the host domain. This comparison is done by the IsValidReferal
function, which returns True
if the domains are the same.
Carrying on, if the IsValidReferer
function returns False
, we call the TerminateRequest
function. All that it does is to prematurely terminate the current Request
. I guess I have provided just enough comments in the code for you to follow without having to bear through my explanation.
Public Class RefererFilter
Implements IHttpModule
Private Application As HttpApplication
Public Sub Init(ByVal Application As HttpApplication)_
Implements IHttpModule.Init
AddHandler Application.BeginRequest, _
AddressOf Me.Application_BeginRequest
End Sub
Public Sub Application_BeginRequest(ByVal sender _
As Object, ByVal events As System.EventArgs)
Application = CType(sender, HttpApplication)
Dim Request As HttpRequest = Application.Context.Request
Dim hostName As String = Request.ServerVariables("HTTP_HOST")
Dim refName As String = Request.ServerVariables("HTTP_REFERER")
If Not refName Is Nothing Then
If Not IsValidReferer(hostName, refName) Then
TeminateRequest()
End If
End If
End Sub
Private Function IsValidReferer(ByRef host As String, _
ByRef referer As String) As Boolean
Return IIf(referer.StartsWith("http://" & host),_
True, False)
End Function
Private Sub TeminateRequest()
Application.CompleteRequest()
End Sub
Public Sub Dispose() Implements IHttpModule.Dispose
End Sub
End Class
Compile this class into an assembly and note the namespace, class and assembly name for registering the filter in the web.config file of your website as follows: -
<httpModules>
<add name="RefererFilter" type="YourNamespace.RefererFilter,YourAssemblyName"/>
</httpModules>
Make sure you replace the type attribute value to an appropriate value. It should reflect this format:
type="YourNamespace.ClassName,AssemblyName"
Code In Action
Case 1) Browse to your website by typing the URL in your browser's address field. Your website should conveniently respond with the contents of the resource that was requested. This happens because HTTP-REFERER
is empty, and the IsValidReferer
function, predictably does not run, and it should not. So if you were at 'x.com' and decided to visit 'your-domain.com' by typing it in to the address bar, 'your-domain' would not know you were previously at x.com. Furthermore, redirecting to a URL in your domain from another domain can be effectively compared to typing in a URL into your browser's address field and pressing GO. So I must warn you that this ploy is not full-proof as such! Innovate!
Case 2) Now we need to have a link on a page part of another domain that points to a URL on your website's domain. Pressing this hyperlink should result in our Filter terminating our request since HTTP_REFERER
contains a value and the IsValidReferer
returns a value of False
. You can modify the code to cause a redirect or do whatever else you can think of to prevent the leeching. So, let's say you went to the website of X (www.x.com) and there's a link to your web page at 'your-domain.com'. You clicked the link and went to 'your-domain.com'. In this case, HTTP_REFERER
tells 'your-domain' that you had come from x.com and the referer filter at 'your-domain.com' will terminate the request. As I had mentioned before, terminating the request completely is not always the best POA (Plan Of Action). A little imagination should make sparks fly!
Conclusion
I suppose combating leech links is a concern for a whole lot of websites. Face it! Nobody enjoys being ripped off. If there is a way to prevent it, do so by all means.
"Prevention is better than cure"
Season's greetings to all!
Jaison John is currently working as a Software Engineer at Net Asset Management,India. He is an Electrical & Electronics engineering graduate from India with over 4 years of development experience behind him.His main areas of interest have been Visual Basic (both version 6 and .NET flavors),ActiveX, DHTML and most recently ASP.net and Winforms Development in C#/VB.net.