Blocking DOS attacks

April 21, 2009

A member of Devon CFUG recently had a problem where one of his sites was being heavily hit by a bot that didn't comply with the crawl-delay setting in robots.txt, which was causing performance issues. After pondering this for a bit a I thought I'd have a go at creating a script to detect and block rogue bots (or something more mailicous). This is what I came up with. It needs to be placed in the onRequestStart method of Application.cfc and you'll need to create the application.remoteIP struct in onApplicationStart.


<cfset var hitsPerSecond = 0 />
<cfset var durationInSeconds = 0 />
<cfset var banrequest = False />

<!---
trap potential DOS attack. Check for:
- multiple requests within a short period of time.
- unknown user-agent
- agent doesn't support http sessions
--->


<!---
First we need to look for suspicious activity before checking requests per second
so that we don't actually create a performance issue by checking!
--->

<cfif CGI.HTTP_CONNECTION EQ "Close">
<!--- compare against a whitelist of allowed bots that comply with the crawl-delay setting in robots.txt--->
<cfif ReFindNoCase( "(Slurp|msnbot|googlebot)", CGI.HTTP_USER_AGENT ) EQ 0>
<!--- unknown agent treat as suspicious --->
<cflock scope="application" timeout="15">
<cfif !StructKeyExists( application.remoteIP, CGI.REMOTE_ADDR )>
<cfset application.remoteIP[ CGI.REMOTE_ADDR ] = { firsthit=Now(), hitcount=1 } />
<cfelse>
<!--- increment hitcount --->
<cfset application.remoteIP[ CGI.REMOTE_ADDR ].hitcount = application.remoteIP[ CGI.REMOTE_ADDR ].hitcount + 1 />
</cfif>

<!--- let the agent have 10 requests before we start checking --->
<cfif application.remoteIP[ CGI.REMOTE_ADDR ].hitcount GT 10>
<!--- agent has exceed 10 hits so need to see how many hits per second --->
<cfset durationInSeconds = DateDiff( 's', application.remoteIP[ CGI.REMOTE_ADDR ].firsthit, Now() ) />

<cfif durationInSeconds GT 0>
<cfset hitsPerSecond = application.remoteIP[ CGI.REMOTE_ADDR ].hitcount / durationInSeconds />
<cfif hitsPerSecond GT 2>
<!--- agent is making multiple requests per second, time to block! --->
<cfset banrequest = True />
</cfif>
</cfif>
</cfif>
</cflock>
</cfif>
</cfif>

<cfif banrequest>
<!--- could add to an array of banned IP addresses instead --->
<cfabort showerror="Our server is current experiencing a high number of connections. Please try again later." />
</cfif>

I should stress that this is just my initial attempt and has not been tested in the wild, but hopefully it might prove interesting.

 


3 comments

  1. >> <cfif application.remoteIP[ CGI.REMOTE_ADDR ].hitcount GT 10>
    >> <cfset durationInSeconds = DateDiff( 's', application.remoteIP[ CGI.REMOTE_ADDR ].firsthit, Now() ) />
    >> <cfif durationInSeconds GT 0>

    isn't it possible for a bot/hacker to send 10+ requests per second?


    >> <cfif !StructKeyExists ...

    interesting! does that actually work? in a <cfset>? i am going to go and try it now!

    Azadi

    Comment by Azadi Saryev – April 21, 2009
  2. Hi John,

    have you had any other feedback on this?

    Comment by Sebastiaan – May 13, 2009
  3. Hi Sebastiaan, no not really. The best solution is probably to use something like the mod_evasive Apache module or get a firewall that can detect and block attacks.

    If those options aren't available, then the code above should work, although I think it would need to clear the application.remoteIP struct if it becomes too large so that it doesn't have an adverse affect on the application.

    Comment by John Whish – May 14, 2009

Leave a comment

If you found this post useful, interesting or just plain wrong, let me know - I like feedback :)

Please note: If you haven't commented before, then your comments will be moderated before they are displayed.