Contents: Securing Network ServicesProxiesSpecific Examples


About this document

10. Apache: modsecurity

From the Web site: ModSecurity is an open source intrusion detection and prevention engine for web applications (or a web application firewall). Operating as an Apache Web server module or standalone, the purpose of ModSecurity is to increase web application security...

10.1. Introductory Material

10.2. Documentation

The reference manual and other documentation can be found at the Mod-Security Web site.

10.3. Download and Installation

10.3.1. Debian

  apt-get install libapache-mod-security

10.3.2. Source

Get the source code from modsecurity.org/download and follow the instructions given in the reference manual.

10.4. Configuration and Loading

First, the module must be loaded by Apache when it starts, so in httpd.conf (or whatever your Apache configuration file is called)

  LoadModule security_module /usr/lib/apache/1.3/mod_security.so
and perhaps
  AddModule mod_security.c

Secondly, the module must be configured, so in http.conf (or whatever...) add

  Include mod_security.conf
or perhaps
<IfModule mod_security.c>
  Include mod_security.conf
</IfModule>
with mod_security.conf
  # This is a simple illustrative example only;  mod_security can do far
  # more --- see the Reference Manual for more.

  # -- PART ONE: filter configuration :

  SecFilterEngine On
      # filter every request

  SecFilterScanPOST On 
      # scan body (or POST) payload (disabled by default)

  SecFilterDefaultAction "deny,log,status:404"
      # sets default action to return 404 (not found) --- the default default
      # is 403 (forbidden)

  # -- PART TWO: rules :

  SecFilter passwd
  SecFilter shadow
      # block requests including "passwd" or "shadow" in the 
      # request string 

10.5. Logs — Testing

Logs appear in /var/log/error.log — using a browser to attempt access to http://mctalby.mc.man.ac.uk/~mc/passwd yields:

  [Wed Jan  4 13:54:54 2006] [error] [client 130.88.201.157] \
      mod_security: Access denied with code 404. Pattern match "passwd" \
      at THE_REQUEST. [hostname "mctalby.mc.man.ac.uk"] [uri "/~mc/passwd"]

10.6. More

mod_security can do much more, including:

10.7. Actions

For each matching rule, mod_security can perform actions:

10.8. Dynamic Firewalling

Use the exec action to execute a script which INSERTs rules into IPTables (or other firewall) which given rules are matched — dangerous. This can be used by those attacking your server to perform a DoS on your machine...

10.9. Regular Expressions

N.B. Apache 1.3 uses POSIX regular expressions; Apache 2.n uses PCRE (Perl-compatible regular expressions).

10.10. Variables

Many such variables exist, such as REMOTE_HOST and REQUEST_METHOD, which correspond to HTTP MIME headers. There are several additional variables, including:


THE_REQUEST The full HTTP request line sent by the browser to the server, e.g., GET /index.html HTTP/1.1. This does not include any additional headers sent by the browser.
REQUEST_URI The resource requested in the HTTP request line. In the example above, this would be /index.html.

10.11. Example: Default Deny

For the truly paranoid, one can implement a default-deny set of rules, like this:

# ----------------------------------------------------------------------------------------------------
# -- Configuration :
# ----------------------------------------------------------------------------------------------------

        ##
        ## see "Configuration" from the "ModSecurity for Apache User Guide" 
        ## for more details
        ##

SecFilterEngine On
    # ...On = analyse every request


SecFilterScanPOST On
    # ...On = turn on scanning of body payload (or POST payload) --- default is off


SecFilterSelective HTTP_Content-Type "!(^$|^application/x-www-form-urlencoded$|^multipart/form-data;)"
    # ...mod_security supports only two types of body:
    #
    #        application/x-www-form-urlencoded (used to transfer form data)
    #        multipart/form-data (used for file transfers)
    #
    #    so allow only these (few web apps use other types)...


SecFilterSelective HTTP_Transfer-Encoding "!^$"
    # ...block chunked-transfer-encoding of requests (not of responses) since mod_security does not
    #    yet support chunked requests (but then nor do any browsers yet either)...


SecFilterDefaultAction "deny,log,status:402"
    # ...default action for a matching rule --- don't accede to the request, log the request (it's
    #    denial, that is) and respond with 404 ("Not found")...
    #
    #    the default default is 403 ("Forbidden") which sounds bad to me (indicates existence)...


SecFilterCheckURLEncoding On
    # ...On = turn on URL encoding checks (block attacks which use %XY where X, Y are _not_ 
    #    in [0-9], [a-f])


SecFilterCheckUnicodeEncoding On
    # ...On = check encoding of characters is correct for UTF-8


##SecFilterForceByteRange 32 126
    # ...This directive allows only one range to be specified. But one can cheat --- filter on
    #    multiple ranges --- e.g. thusly:
    #
    #        SecFilterSelective THE_REQUEST "!^[\x0a\x0d\x20-\x7e]+$"
    #
    #     ...allows characters 10, 13 and 32-126...
    #
    # http://www.ascii.cl/htmlcodes.htm :
    #
    #  32 -- 126
    #  NOT 127
    #  NOT 128 -- 159
    #  160 -- 255
    #
SecFilterSelective THE_REQUEST "!^[\x20-\x7e\xa0-\xff]+$"


# ----------------------------------------------------------------------------------------------------
# -- Rules to block directory traversal :
# ----------------------------------------------------------------------------------------------------

SecFilter "\.\./"
    # ...N.B. this matches /path/a../thing but not /patch/../thing because
    #         (quoting Ivan Ristic) Apache is normalizing the path before
    #         mod_security gets to it (you can see it in the debug log if 
    #         you increase the verbosity of the log). If you try something 
    #         like:
    #             /cgi-bin/modsec-test.pl?p=123/../456
    #         it will work. Apache only normalizes the data on the
    #         left hand of the question mark character.

# ----------------------------------------------------------------------------------------------------
# -- Default deny :
# ----------------------------------------------------------------------------------------------------



# -- commands such as 
#
#        cat /var/log/apache/access.log | awk '{print $6" "$7" "$8}' | sort -u
#
#    which yields
#
#        "GET / HTTP/1.0"
#        "GET / HTTP/1.1"
#        "GET /Password HTTP/1.0"
#        "GET /Password HTTP/1.1"
#        "GET /Password/ HTTP/1.0"
#        "GET /Password/ HTTP/1.1"
#        "GET /Password/place.jpg HTTP/1.0"
#        "GET /Password/place.jpg HTTP/1.1"
#        "GET /icons/apache_pb.png HTTP/1.0"
#        "GET /icons/apache_pb.png HTTP/1.1"
#        "GET /icons/debian/openlogo-25.jpg HTTP/1.0"
#        "GET /icons/debian/openlogo-25.jpg HTTP/1.1"
#        .
#        .
#
#    and
#
#        cat /var/log/apache/access.log | awk '{print "SecFilterSelective REQUEST_URI \"^"$7"$\" allow"}' | sort -u
#
#    which writes out ready-made rules and
#
#        cat /var/log/apache/error.log | grep "Warning. Pattern match \".\"" | awk '{print $19}' | sort -u
# 
#    are useful in implementing rule-sets.

SecFilterSelective REQUEST_URI "^?$",                              allow
SecFilterSelective REQUEST_URI "^?Password$",                      allow
SecFilterSelective REQUEST_URI "^?Password/$",                     allow
SecFilterSelective REQUEST_URI "^?Password/place.jpg$",            allow
SecFilterSelective REQUEST_URI "^?icons/apache_pb.png$",           allow
SecFilterSelective REQUEST_URI "^?icons/debian/openlogo-25.jpg$",  allow
SecFilterSelective REQUEST_URI "^?icons/jhe061.png$",              allow
SecFilterSelective REQUEST_URI "^?layers.js$",                     allow
SecFilterSelective REQUEST_URI "^?manchester_logo_final.gif$",     allow
SecFilterSelective REQUEST_URI "^?password/$",                     allow
SecFilterSelective REQUEST_URI "^?selfreg.css$",                   allow
SecFilterSelective REQUEST_URI "^/Password/passform.pl$",          allow


SecFilterSelective REQUEST_URI ".", deny


# ----------------------------------------------------------------------------------------------------
# Time for a nice cup of tea.
# ----------------------------------------------------------------------------------------------------


...previousup (conts)next...