0

What is the best way to detect a DOS attack and attackers from a direct website (in this case ASP.NET MVC) like ApplicationBeginRequest.

I have searched on this site and found a similar question without any answer or examples. Google didn't find any results either.

Restrict IP's by code in Asp.net MVC

I know the dynamic IP address restriction on IIS, but I would like the website to detect such attacks and send an e-mail alerts.

Unfortunately I don't have access to my IIS logs because my site is on shared hosting.

Community
  • 1
  • 1
HolloW
  • 720
  • 11
  • 21
  • 3
    In traffic terms, one person's DDOS is another person's cash-cow. Define DDOS. An HTTP based DOS attack would be quite a weak way of taking down a site. Supposing you have a large # of clients feeding through a single proxy? Is that one bad client or many good clients? How do you tell the difference? Usually DDOS is effected via means like NTP/DNS amplification. As such, trying to prevent it at HTTP level is misguided. Are you looking for a rate-limiter? Per IP? – spender Jun 25 '14 at 12:54
  • @Infer-On of course the firewall do this job, but I don't manage the firewall at the moment so I need a way to detect strange activity. – HolloW Jun 25 '14 at 13:11
  • @spender Yep, I look for a rate-limiter or a alert-limiter to get more info about the different request on the site. Something like the user 1.2.3.4 make 1000 request in the last 15 seconds so this is considered a strange activity. – HolloW Jun 25 '14 at 13:15
  • 1
    @HolloW : That's not ***distributed***, that's a single user on a single IP. – spender Jun 25 '14 at 13:40
  • @spender Yes, it's true, my mistake. I just edit the post to match to my current doubt. Thanks for point it out. – HolloW Jun 25 '14 at 13:47

2 Answers2

2

DDOS attack is the Distributed Denial Of Service. If it is done in a clever manner(with semisyncronized botnet that does not spam you madly but uses intermittent request flooding) then I am not sure if there really exists a good way to detect it. Detecting DDoS demands heavy statistical analyses, and the attacker, knowing what and how is being analyzed, can tinker his botnet to slip through the defenses.

Well, you can detect that your site is overloaded when there are N-amount of requests per minute or something similar(MSDN, StackOverflow). But what will you do - just abort them all and ban IPs during the overload? It is difficult to analyze and prevent DDoS in a reliable manner without collateral damage.

Community
  • 1
  • 1
Eugene Podskal
  • 10,270
  • 5
  • 31
  • 53
  • At the moment I only need the information to alert by e-mail, then I will block the ip's from the dynamic restriction on IIS. – HolloW Jun 25 '14 at 13:21
  • I've tried to elaborate that simple checks(see links in the post) can detect only very simple DoS attacks. But no one stops your from adding such a logic into your app. Just be ready for possible restrictions and consequences. – Eugene Podskal Jun 25 '14 at 13:25
2

With the caveat of my comments to your question (i.e. this won't actually prevent DDOS because DDOS is likely to happen at a lower level in the networking stack), here's a rate limiter based on the leaky bucket abstraction that's served my purposes well over the last few years:

public class RateLimiter
{
   private readonly double numItems;
   private readonly double ratePerSecond;
   private readonly ConcurrentDictionary<object, RateInfo> rateTable = 
                             new ConcurrentDictionary<object, RateInfo>();
   private readonly double timePeriod;

   public RateLimiter(double numItems, double timePeriod)
   {
       this.timePeriod = timePeriod;
       this.numItems = numItems;
       ratePerSecond = numItems / timePeriod;
   }

   public double Count
   {
       get
       {
           return numItems;
       }
   }

   public double Per
   {
       get
       {
           return timePeriod;
       }
   }

   public bool IsPermitted(object key)
   {
       var permitted = true;
       var now = DateTime.UtcNow;

        rateTable.AddOrUpdate(
            key,
            k => new RateInfo(now, numItems - 1d),
            (k, rateInfo) => {
                var timePassedSeconds = 
                                  (now - rateInfo.LastCheckTime).TotalSeconds;
                var newAllowance = 
                                  Math.Min(rateInfo.Allowance 
                                            + timePassedSeconds 
                                            * ratePerSecond,
                           numItems);
                if (newAllowance < 1d)
                {
                    permitted = false;
                }
                else
                {
                    newAllowance -= 1d;
                }
                return new RateInfo(now, newAllowance);
            });
        var expiredKeys = rateTable
               .Where(kvp => 
                   (now - kvp.Value.LastCheckTime) > 
                   TimeSpan.FromSeconds(timePeriod))
               .Select(k => k.Key);
        foreach (var expiredKey in expiredKeys)
        {
            Reset(expiredKey);
        }

       return permitted;
   }

   public void Reset(object key)
   {
        RateInfo rr;
        rateTable.TryRemove(key,out rr);
   }


   internal struct RateInfo
   {
       private readonly double allowance;
       private readonly DateTime lastCheckTime;

       public RateInfo(DateTime lastCheckTime, double allowance)
       {
           this.lastCheckTime = lastCheckTime;
           this.allowance = allowance;
       }

       public DateTime LastCheckTime
       {
           get
           {
               return lastCheckTime;
           }
       }

       public double Allowance
       {
           get
           {
               return allowance;
           }
       }
   }
}

Make sure your keys are suited to storage in a hashtable (e.g. they implement equality and .GetHashCode) and use it like this:

void Main()
{
    var limiter=new RateLimiter(5,1);   //permit 5 items every 1 second
    AddStuff(limiter);
    Thread.Sleep(10000);
}
async Task AddStuff(RateLimiter limiter)
{
    for(var i=0;i<10000;++i)
    {
        Console.WriteLine(limiter.IsPermitted("foo"));
        await Task.Delay(10);

    }
}
spender
  • 117,338
  • 33
  • 229
  • 351
  • Nice idea, do you use this on your website, right? I will use this on the ApplicationBeginRequest to limit the requests and create the object on the Application_Start. Do you use in the same way o differently? – HolloW Jun 26 '14 at 11:45
  • @HolloW: We use it to rate-limit specific resources (at controller level) rather than all resources (via `ApplicationBeginRequest`). Remember that this won't scale if you use more than one web-server, and I do wonder how it will play with caching. It's probably worth remembering that a properly cached page will not cause that much load and I've never run into issues with cached pages. It's probably better to rate limit non-cachable content/uploads etc. that are expensive to process. As such, I think a more fine-grained approach combined with extensive caching is probably more sensible. – spender Jun 26 '14 at 17:28
  • Just seen this answer. Perfect idea and implementation. I like it very much because this can be used in other scenarios such as preventing auto submission of forms that sends emails etc.. – Subliminal Hash Apr 03 '15 at 12:52