Warding Off IP-based Web Scans

By Paul Heinlein | Oct 2, 2014

Like any other web server on the Internet, mine is frequently the target of IPv4-based scanning attacks. The scanner just polls address after address and starts looking for vulnerabilties (or whatever) as soon as it sees signs of life on TCP port 80.

Apache has a quirk that allows you to mostly ignore these scanners, as long as you’re willing to use a virtual host for your web service.

The example below is for Apache version 2.4, but it needs only slight modification with work with 2.2. I tend to keep all my Apache configuration directives in one file rather than using a number of Include directives, but the theory is the same regardless of the approach you use.

The quirk of which you’ll take advantage is this: the first-listed <VirtualHost> block becomes the default for any client that doesn’t provide a proper HTTP Host: header. So any IP-based query will be caught here. Essentially, you’re going to make it a deny-everyone host (with an exception for localhost and perhaps an administrative IP address):

# ... all your preliminary configuration happens first, then ...
   
# the first-listed <VirtualHost> is the default if a client doesn't
# provide a Host: header in the request. typically, these requests
# are IPv4-based scanners -- so we reject them outright.
<VirtualHost *:80>
  ServerSignature off
  # use special logs for this host, just in case we want to use
  # a utility like fail2ban to firewall the scanners
  CustomLog logs/dummy-access.log
  ErrorLog logs/dummy-error.log
  <Location />
    <RequireAny>
      Require local
      Require ip aaa.bbb.ccc.ddd # your admin host
    </RequireAny>
  </Location>
</VirtualHost>
   
# now we can get on with the real work
<VirtualHost *:80>
  ServerName your.web.com
  # et cetera, et cetera, et cetera
</VirtualHost>

Adding a fail2ban rule

If the scanbots really get under your skin, and your web server runs on a reasonably full-featured Linux distribution, you can use fail2ban to add scanners’ IP addresses to your host firewall.

If, like me, you don’t want to mistakenly ban a legitimate host, then I’ve found the best thing to do is to specify (as above) log files specifically for your first virtual host. Only malicious or really badly written clients will be listed in there; all legitimate hosts will provide a Host: header.

Here’s the filter that has worked for me so far with Apache 2.4:

# /etc/fail2ban/filter.d/apache-iponly.conf
  
[DEFAULT]
  
_apache_error_msg = \[[^]]*\] \[\S*:error\] \[pid \d+\] \[client <HOST>(:\d{1,5})?\]
  
[Definition]
  
failregex = ^%(_apache_error_msg)s (AH0\d+: )?client denied by server configuration: (uri )?.*$
            ^%(_apache_error_msg)s script '\S+' not found or unable to stat(, referer: \S+)?\s*$
  
ignoreregex = 
  
# DEV Notes:
#
# the web server only responds to clients with a valid Host:
# header. anyone who tries using IP only will get shunted into
# the dummy-error.log and get a client-denied message
#
# the second regex catches folks with otherwise valid CGI paths but no
# good Host: header
#
# Author: Paul Heinlein

Then in your jail.local file, add the related jail definition, e.g.,

[apache-iponly]
enabled  = true
logpath  = /var/log/httpd/dummy-error.log
port     = 80
filter   = apache-iponly
maxretry = 1

I use a very low maxretry number because no reasonable client will end up in that log.