Hack #117. Detect and Prevent Web Application Intrusions

Protect your web server and dynamic content from intrusions.

Network intrusion detection systems are well suited to detecting intrusions that utilize common protocols and services, such as those used by web applications. However, due to the complexity of these applications and the variety of attacks they are vulnerable to, it can be difficult to detect and prevent intrusions without generating many false positives. This is especially true for web applications that use SSL, since this requires you to jump through hoops to enable the NIDS to actually get access to the unencrypted traffic coming to and from the web server.

One way to get around these issues is to integrate the intrusion detection system into the web server itself. This is just what mod_security (http://www.modsecurity.org) does for the popular Apache (http://www.apache.org) web server.

mod_security, as the name suggests, is a module for the Apache web server that is meant to increase its security by providing facilities for filtering requests and performing arbitrary actions based on user-specified rules. In addition, mod_security performs various sanity checks that normalize the requests that the web server receives. With the proper filtering rules, mod_security can defeat directory traversal, cross-site scripting, SQL injection, and buffer overflow attacks.

To install mod_security, download and unpack the source distribution. If you want to install it as a DSO (i.e., a module), you can do so easily with the apxs utility. First, change to the directory appropriate for the version of Apache that you are using: apache1 or apache2. Then, run a command like this:

# apxs -cia mod_security.c
            

This compiles mod_security and configures Apache to load it at startup. If you want to statically compile mod_security, you will have to rebuild Apache. If you are using Apache 1.x, you can compile it statically by copying mod_security.c to the src/modules/extra directory in the Apache source tree. Then, when you run Apache’s configure script, use these command-line switches:

--activate-module=src/modules/extra/mod_security
--enable-module=security

For Apache 2.x, copy mod_security.c from the apache2 directory to the modules/proxy directory in the Apache 2.x source tree. Then, use these command-line switches when running the configure script:

--enable-security 
--with-module=proxy:mod_security.c

Once you’ve installed mod_security, you’ll need to enable it. You can do this by putting the following lines in your httpd.conf file:

<IfModule mod_security.c>
    SecFilterEngine On
</IfModule>

This enables the request normalization features of mod_security for all requests made to the web server. When mod_security is enabled, it intercepts all requests coming into the web server and performs several checks on them before passing the requests through any user-defined filters and finally either servicing or denying them.

During these sanity checks, mod_security converts several types of evasive character sequences to their more commonly used equivalent forms. For example, it transforms the character sequences // and /./ to /, and on Windows, it converts the \ character to /. It also decodes any URL-encoded characters.

In addition to these checks, you can configure mod_security to scan the payload of POST method requests and to validate URL and Unicode encoding within requests. To enable these features, add these lines to your httpd.conf:

SecFilterScanPOST On
SecFilterCheckURLEncoding On
SecFilterCheckUnicodeEncoding On

URL encoding allows someone making a request to encode characters by using hexadecimal values, which use the numbers 0 through 9 and the letters A through F, prefixed by the % character. When URL-encoding validation is enabled, mod_security simply ensures that any URL-encoded characters don’t violate the hexadecimal numbering system. Similarly, when performing Unicode validation, mod_security ensures that the string seen by the web server in fulfilling the request is a valid Unicode string. Unicode validation is useful if your web server is running on an operating system that supports Unicode or your web application makes use of it.

To avoid buffer overflow exploits, you can also limit the range of bytes that are allowed in request strings. For instance, to allow only printable characters (and not ones that might show up in exploit shell code), add a line like this to your httpd.conf file:

SecFilterForceByteRange 32 126

You can create user-defined filters with either the SecFilter or the SecFilterSelective keyword. Use SecFilter to search just the query string, or use SecFilterSelective if you would like to filter requests based on the value of an internal web server variable. Both of these filtering keywords can accept regular expressions.

Let’s look at a few filtering rules can help prevent some common attacks.

This rule filters out requests that contain the character sequence ../:

SecFilter "\.\./"

Even though the web server will interpret the ../ correctly and disallow access if it ends up resolving to something outside of its document root, that might not be the case for scripts or applications that are on your server. This rule prevents such requests from being processed.

Cross-site scripting (XSS) attacks are invoked by inserting HTML or JavaScript into an existing page so that other users will execute it. Such attacks can be used to read a user’s session cookie and gain full control of that user’s information. You can prevent these attacks by having mod_security filter out requests that contain JavaScript.

To disallow JavaScript in requests, use a rule like this:

SecFilter "<[[:space:]]*script"

However, there are many other ways in which JavaScript can be inserted into a request. It is safer to simply disallow HTML, which can be done by using this rule:

SecFilter "<.+>"

SQL injection attacks are similar to XSS attacks, except in this case attackers modify variables that are used for SQL queries in a way that enables them to execute arbitrary SQL commands.

This rule prevents SQL injection in a cookie called sessionid:

SecFilterSelective COOKIE_sessionid "!^(|[0-9]{1,9})$"

If a sessionid cookie is present, the request can proceed only if the cookie contains one to nine digits.

This rule requires HTTP_USER_AGENT and HTTP_HOST headers in every request:

SecFilterSelective "HTTP_USER_AGENT|HTTP_HOST" "^$"

You can search on multiple variables by separating each variable in the list with a | character. Attackers often investigate using simple tools (even telnet) and don’t send all headers, as browsers do. Such requests can be rejected, logged, and monitored.

This rule rejects file uploads:

SecFilterSelective "HTTP_CONTENT_TYPE" multipart/form-data

This is a simple but effective protection, rejecting requests based on the content type used for file upload.

Again, manual requests frequently do not include all HTTP headers. This rule logs requests without an Accept header, so you can examine them later:

SecFilterSelective "HTTP_ACCEPT" "^$" log,pass

The Keep-Alive header is another good candidate. Notice that in addition to the variable and search string this rule contains the keywords log and pass, which specify the actions to take if a request matches the rule. In this case, any requests that match will be logged to Apache’s error log, and then the request will go on for further processing by the web server. If you do not specify an action for a filter rule, the default action will be used.

You can specify the default action like this:

SecFilterDefaultAction "deny,log,status:500"

If you set this as the default action, the web server will deny all requests that match filter rules and that do not specify a custom action. These requests will be logged and then redirected to an HTTP 500 status page, which will inform the client that an internal server error occurred. Other possible actions include allow, which is similar to pass but stops other filters from being tried; redirect, which redirects the client to an arbitrary URL; exec, which executes an external binary or script; and chain, which allows you to effectively AND rules together.

In addition to filtering, mod_security provides extensive auditing features, allowing you to keep logs of all the requests sent to the server. To turn on audit logging, add lines similar to these to your httpd.conf:

SecAuditEngine On
SecAuditLog logs/audit_log

Bear in mind, though, that this option can generate quite a lot of data very quickly. To log only requests that trigger a filter rule, set the SecAuditEngine variable to RelevantOnly. Alternatively, you can set this variable to DynamicOrRelevant, which will log requests for dynamic content as well as requests that trigger a filter rule.

As with most other Apache configuration directives, you can enclose mod_security configuration directives within a <Location> tag to specify individual configurations for specific scripts or directory hierarchies.

For more examples of useful mod_security rules, be sure to look at modsecurity-rules-current.tar.gz, which is available from the mod_security download page and includes an extensive list of rules that can be included in your Apache configuration using the Include directive.

mod_security is a powerful tool for protecting your web applications, but it should not take the place of actually validating input in your application or other secure coding practices. If possible, it is best to use a tool such as mod_security in addition to employing such methods.