ActionValidator Class - ASP.NET

This page is a Draft. Its content is not complete and might contain errors.

Adapted from this article on The Code Project's website.

Overview

I have a class named ActionValidator which maintains a count of specific actions like First Visit, Revisit, Asynchronous postbacks, Add New widget, Add New Page etc. It checks whether the count for such specific action for a specific IP exceeds the threshold value or not. The enumeration contains the type of actions to check for and their threshold value for a specific duration: 10 mins.

A static method named IsValid does the check. It returns true if the request limit is not passed, false if the request needs to be denied. Once you get false, you can call Request.End(). The cache key is built with a combination of action type and client IP address. First it checks if there's any entry for the action and the client IP in cache or not. If not, start the count and remember the count for the IP in cache for the specific duration. The absolute expiration on cache item ensures that after the duration the cache item will be cleared and the count will restart. When there's already an entry in the cache, get the last hit count, and check if the limit is exceeded or not. If not exceeded, increase the counter. There is no need to store the updated value in the cache again by doing: Cache[url]=hit; because the hit object is by reference and changing it means it gets changed in the cache as well. In fact, if you do put it again in the cache, the cache expiration counter will restart and fail the logic of restarting count after specific duration.

Of course you can put in some Cisco firewall and prevent DOS attack. You will get a guarantee from your hosting provider that their entire network is immune to DOS and DDOS (Distributed DOS) attacks. What they guarantee is network level attack like TCP SYN attacks or malformed packet floods etc. There is no way they can analyze the packet and find out a particular IP is trying to load the site too many times without supporting cookie or trying to add too many widgets. These are called application level DOS attack which hardware cannot prevent. It must be implemented in your own code.

Source Code

HitInfo Class

VB.NET

{copytext|VbHitInfo}
'TODO


C#

{copytext|CsHitInfo}
public class HitInfo
{
    private int _hits;

    public HitInfo() {}

    public int Hits
    {
        get { return _hits; }
        set { _hits = value; }
    }
}

ActionValidator Class

VB.NET

{copytext|VbActionValidator}
'TODO


C#

{copytext|CsActionValidator}
public static class ActionValidator
{
    private const int DURATION = 10; // 10 min period
 
    public enum ActionTypeEnum
    {
        FirstVisit = 100, // The most expensive one, choose the value wisely. 
        ReVisit = 1000,  // Welcome to revisit as many times as user likes
        Postback = 5000,    // Not must of a problem for us
        AddNewWidget = 100, 
        AddNewPage = 100,
    }

    public static bool IsValid( ActionTypeEnum actionType )
    {
       HttpContext context = HttpContext.Current;
       if( context.Request.Browser.Crawler ) return false;
       
       string key = actionType.ToString() + context.Request.UserHostAddress;
       var hit = (HitInfo)(context.Cache[key] ?? new HitInfo());
         
       if( hit.Hits > (int)actionType ) return false;
       else hit.Hits ++;
        
       if( hit.Hits == 1 )
          context.Cache.Add(key, hit, null, DateTime.Now.AddMinutes(DURATION), 
             System.Web.Caching.Cache.NoSlidingExpiration, 
             System.Web.Caching.CacheItemPriority.Normal, null);
       return true;
    }

Implementation

Usage in an ASPX page or Master page.

VB.NET

{copytext|VbImplementation}
'TODO


{copytext|CsImplementation}
protected override void OnInit(EventArgs e)
{
    base.OnInit(e);

    if(base.IsPostBack) 
        if(!ActionValidator.IsValid(ActionValidator.ActionTypeEnum.Postback))  
            Response.End();
    else if (IsFirstVisit())
        if(!ActionValidator.IsValid(ActionValidator.ActionTypeEnum.FirstVisit))  
            Response.End();
    else if(!ActionValidator.IsValid(ActionValidator.ActionTypeEnum.ReVisit)) 
      
        Response.End();
}
private bool IsFirstVisit()
{
    if (Session["IsFirstVisit"] == null)
    {
        Session["IsFirstVisit"] = false;
        return true;
    }
    else
        return false;
}