Hack 93 Detect and Prevent Web Application Intrusions

figs/moderate.gif figs/hack93.gif

Protect your web server and dynamic content from intrusions.

Detecting intrusions that utilize common protocols and services is a job that a network intrusion detection system is well suited for. However, due to the complexity of web applications and the variety of attacks they can be vulnerable to, it is more 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 the security of a web server by providing facilities for filtering requests and performing arbitrary actions based on user-specified rules. In addition, mod_security will also perform various sanity checks that normalize the requests that the web server receives. With the proper filtering rules, mod_security can be effective at defeating directory traversal, cross-site scripting, SQL injection, and buffer overflow attacks.

To install mod_security, download and unpack the source distribution. If you wish 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 will compile mod_security and configure Apache to load it at startup. If you would like 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:



Now that mod_security has been installed, 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


This will enable the request normalization features of mod_security for all requests made to the web server. Alternatively, you can enable it only for dynamic content by setting the SecFilterEngine variable to DynamicOnly. When mod_security is enabled, it will intercept all requests coming into the web server and perform several checks on it before passing it through any user-defined filters and finally either servicing or denying the requests. During these sanity checks, mod_security will convert several different types of evasive character sequences to their more commonly used equivalent forms. Thus the character sequences // and /./ will be transformed to /, and on Windows the \ character will be converted to /. In addition, any URL-encoded characters will be decoded. In addition to these checks, mod_security can also be configured to scan the payload of POST method requests and validate URL encoding and Unicode encoding contained 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. When performing Unicode validation, mod_security basically does the same type of thing?ensure 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:

SecFilterForceByteRange 32 126

User-defined filters are created with either the SecFilter or the SecFilterSelective keyword. You can use SecFilter to search just the query string, or you can 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.

The following are filtering rules that can help prevent some common attacks.

The following rule will filter 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 may 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"

In addition, you can disallow HTML by using this rule:

SecFilter "<(.|\n)+>"

SQL-injection attacks are similar to XSS attacks, except in this case attackers modify a variable that is used for an SQL query in such a way that they can execute arbitrary SQL commands.

To protect against this class of attacks, you can employ rules similar to these:

SecFilter "delete[[:space:]]+from" 

SecFilter "insert[[:space:]]+into" 

SecFilter "select.+from"

This rule prevents SQL injection in a cookie called sessionid:

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

If a sessiondid 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.

This rule logs requests without an Accept header, so you can examine them later:

SecFilterSelective "HTTP_ACCEPT" "^$" log,pass

Again, manual requests frequently do not include all HTTP headers. 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. In addition, they 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 are 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 the full request sent to the server. To turn on audit logging, add lines similar to these to your httpd.conf:

SecAuditEngine On

SecAuditLog logs/audit_log

However, this will log all requests sent to the web server. Obviously, this can generate quite a lot of data very quickly. To only log requests that triggered a filter rule, set the SecAuditEngine variable to RelevantOnly. Alternatively, you can set this variable to DynamicOrRelevant, which will log requests to dynamic content or requests that triggered 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.

mod_security is a very 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 at all possible, it is best to employ such methods in addition to using a tool such as mod_security.

See Also

  • "Introducing mod_security": http://www.onlamp.com/pub/a/apache/2003/11/26/mod_security.html

  • Mod_security Reference Manual v1.7.4: http://www.modsecurity.org/documentation/modsecurity-manual-1.7.4.pdf