Block access to images from other websites

In ASP.NET you can write HTTP Modules to perform actions inside the request pipeline before the request is being finally processed. In this example we use an HTTP Module to check whether the REFERER header matches our website. If it doesn’t then we won’t allow access to the resources. By doing this we can simply block access from all other websites.

Please note that the REFERER header is not to be trusted (see the end of this post for more info). We use it here in a very simple way, but in a production environment it should rather be used for logging then for blocking access.

This is a little of what Wikipedia has to say about the REFERER header:

REFERER: This is the address of the previous web page from which a link to the currently requested page was followed. (The word “referrer” has been misspelled in the RFC as well as in most implementations to the point that it has become standard usage and is considered correct terminology)

When you create your own HttpModule, there is a long list of available events to hook on to. Since I wanted to block a call completely I hooked on to a very early one – BeginRequest. At this stage, the HttpContext.Request object isn’t available (use the PostAcquireRequestState event if you need that object). However, there are ways to access the data anyway, as seen in the code below. A good list of the available events can be found here.

The code is very simple. We define the “approved hosts” and the file extensions we want to check. Then we check if the request matches an approved request or not. If it doesn’t then we set a status code and end the request. If the request is approved we don’t need to do anything, we just let it pass.

public class VerifyCallingSourceModule : IHttpModule
{
  public void Dispose()
  {
  }

  public void Init(HttpApplication context)
  {
    // This is the first event in the request chain
    context.BeginRequest += new EventHandler(OnBeginRequest);
  }

  void OnBeginRequest(Object sender, EventArgs e)
  {
    var approvedHosts = new List<string>() { "127.0.0.1", "www.mywebsite.com" };
    var extensionsToCheck = new List<string>() { ".jpg", ".gif" };

    string extension = Path.GetExtension(System.Web.HttpContext.Current.Request.Url.ToString()).ToLower();
    if (extensionsToCheck.Contains(extension))
    {
      Uri referrer = System.Web.HttpContext.Current.Request.UrlReferrer; 
      if (approvedHosts.Contains(referrer.Host.ToLower()))
      {
        // This host is approved
      }
      else
      {
        // Host referrer exists and it's not in the approved list
        HttpApplication context = sender as HttpApplication;
        context.Response.StatusCode = 404;
        context.Response.SuppressContent = true;
        context.Response.End();
      }
    }
  }
}

In my example above I choose to return a 404 - File Not Found status when access was blocked. But you can return other codes as well. I list of available status codes can be found here.

When the module is done we register it in Web.config under <system.webServer>.

<modules>
  <add name="VerifyCallingSourceModule" type="MyWebApplication.Modules.VerifyCallingSourceModule" />
</modules>

You also need to change the routing so ASP.NET will let even existing static files be routed and thus be exposed to your http module.

public static void RegisterRoutes(RouteCollection routes)
{
  routes.RouteExistingFiles = true;

  // ....
}

That’s all you need to do.

Be careful
The code above is a way to illustrate what you can do with an HttpModule. However, there are many reasons to be careful when you’re using the REFERER header.

  • Most importantly – NEVER use this as a way of authentication or authorization! The code above uses the REFERER header and this header can easily be manipulated by the client. You can read more here of why it’s considered to be a vulnerability.
  • When switching between HTTP and HTTPS, the REFERER header is usually not being sent from your browser. This code would then block legitimate users.
  • A corporation proxy might filter out this header for privacy reasons.

Steps to make this function more usable in production

  • As mentioned above, the REFERER header can never be trusted and can be manipulated. However, many bloggers etc that just want to link to your pictures have no idea of how to do that and a simple function like the one above can be used to block them from doing that and letting you pay for the increased traffic. You do need to take into consideration all the occasions where no REFERER header is available and maybe let all of them slip through, just so not a bunch of your users will be sitting there without any pictures…quite booring
  • Another way of doing this is to the check an authentication cookie after the PostAuthenticateRequest event. You can then make sure that only users logged in will have access to certain resources. However, all the unauthenticated users on your front page will be refrained from access. In some circumstances this might be exactly what you want.