Sécurité : Linux : HTTP DDoS/flood attacks mitigation with ModSecurity

ModSecurity is a module most often used only for string/pattern matching. We will see that it can also do much more than that and be used, for instance, to mitigate or block HTTP floods in an even more effective manner than modules like mod_evasive.

 this article deals only with HTTP D(D)oS, ie, multiple requests on one or more pages of your HTTP server. Regarding SYN flood attacks, please refer to Floodmon.

To reject or blacklist IP's that have too many connection attempts, here are 5 actions and 1 variable needed for that purpose.

  • initcol : used to initialize a persistant collection. Currently, only 3 types of collections are recognized by ModSecurity : IP, SESSION et USER. The first one will be used for connection tracking (client IP adress) :
  •   initcol:ip=%{REMOTE_ADDR}

  • setvar : creates, deletes or uptades a variable in a collection. To count the number of requests from the same IP, we will create a ddos variable for the ip collection and will increment it (+1) each time we will see that IP :
  •   setvar:ip.ddos=+1

  • deprecatevar : decrements a variable counter according to its age. The counter is an interger and always positive (if counter == 10 and is decreased by 20 units, it will simply be reset to 0). We will use it to decrement our ip.ddos variable by 100 units every 10 seconds :
  •   deprecatevar:ip.ddos=100/10

  • drop : close the connection and 'bypass' Apache HTTP response by sending only a TCP FIN/ACK segments to the request made by the client. Here is a capture showing 2 'GET / HTTP/1.0' requests blocked by the drop action. The returned FIN/ACK segments are shown with x signs :
  • Instead of sending a HTTP error page and code (usually 403) just like does the deny action, drop is useful to save system resources and bandwidth by ignoring the request.
    We will use it for 'suspicious' IPs, that is those having a lot of connections but not enough to confirm that they are part of a flood, DoS or DDoS attack. This will prevent to blacklist by mistake a (nervous) user who is loading to many webpages from the website into a lot of different tabs with his browser. In our example, any single IP with more than 25 requests per 10 seconds will be dropped and, as seen with deprecatevar, its counter will be decremented by 100 units after that time, allowing this user to calm down a bit :

      SecRule IP:DDOS "@gt 25" "nolog,drop"

     Important :
    - drop does not work with Windows versions of mod_security !
    - it is better to use nolog here, otherwise every single drop action will be recorded and could flood the log files.

  • exec : allows to execute an external command/script. No parameter can be passed to the program, however all environment variables will be forwared to it. We will use it to blacklist any IP with more than 50 requests per 10 seconds by calling a small Perl client/server that will block the IP with iptables. The program and instructions can be found here : communication between mod_security and iptables to block IP's.
    We can log that action without risking to flood the logs as the IP will be blocked almost right away with iptables.

      SecRule IP:DDOS "@gt 50" "log,exec:/var/www/modsec2ipt.pl"

  • REQUEST_LINE : variable used to select the HTTP request that needs to be filtered. For our example, we will filter 'GET' requests calling only one of these pages : '/' root page, *.html, *.php, *.cgi and *.pl pages, using HTTP protocol, whatever version. Thus we will not track connections and requests to get images, CSS and JS files and will avoid to blacklist a user by mistake :
  •   SecRule REQUEST_LINE "^GET (?:/|.+\.html|.+\.php|.+\.cgi|.+\.pl) HTTP"

    Generic rule :

      SecAction initcol:ip=%{REMOTE_ADDR},nolog
      SecRule REQUEST_LINE "^GET (?:/|.+\.html|.+\.php|.+\.cgi|.+\.pl) HTTP" \
      SecRule IP:DDOS "@gt 50" "log,exec:/var/www/modsec2ipt.pl"
      SecRule IP:DDOS "@gt 25" "nolog,drop"

    Remember, of course, to adapt this rule to your server (REQUEST_LINE and threshold limit).