Special Features
Prev | Home | Next

## Special Features

### File upload support

ModSecurity is capable of intercepting files uploaded through `POST` requests and `multipart/form-data` encoding or (as of 1.9) through `PUT` requests.

#### Choosing where to upload files

ModSecurity will always upload files to a temporary directory. You can choose the directory using the `SecUploadDir` directive:

    SecUploadDir /tmp

It is better to choose a private directory for file storage, somewhere only the web server user is allowed access. Otherwise, other server users may be able to access the files uploaded through the web server.

#### Verifying files

You can choose to execute an external script to verify a file before it is allowed to go through the web server to the application. The `SecUploadApproveScript` directive enables this function. Like in the following example:

    SecUploadApproveScript /full/path/to/the/script.sh

The script will be given one parameter on the command line - the full path to the file being uploaded. It may do with the file whatever it likes. After processing it, it should write the response on the standard output. If the first character of the response is "1" the file will be accepted. Anything else, and the whole request will be rejected. Your script may use the rest of the line to write a more descriptive error message. This message will be stored to the debug log.

#### Storing uploaded files

You can choose to keep files uploaded through the web server. Simply add the following line to your configuration:

    SecUploadKeepFiles On

Files will be stored at a path defined using the `SecUploadDir` directive. If you want to keep files selectively you can use

    SecUploadKeepFiles RelevantOnly

This will keep only those files that belong to requests that are deemed relevant.

#### Interacting with other daemons

To allow for interaction with other daemons (for example `ClamAV`, as described later), as of 1.9dev1 files are created with relaxed permissions allowing group read. To do this assuming Apache runs as httpd and daemon as clamav:

    # mkdir /tmp/webfiles
    # chown httpd:clamav /tmp/webfiles
    # chmod 2750 /tmp/webfiles

With this configuration in place, the user clamav will have access to the folder. The same goes for files, which will be created with group ownership clamav. Don't forget to use the `SecUploadDir` directive to store files in `/tmp/webfiles.`

### Note

If you are keeping files around it might not be safe to leave them there owned by the web server user. For example, if you have PHP running as a module and untrusted users on the server they may be able to access the files. Consider implementing a cron script to make the files only readable by root, and possibly move them to a separate location altogether.

#### Integration with ClamAV

ModSecurity includes a utility script that allows the file approval mechanism to integrate with the ClamAV virus scanner. This is especially handy to prevent viruses and exploits from entering the web server through file upload.

    #!/usr/bin/perl
    #
    # modsec-clamscan.pl
    # mod_security, http://www.modsecurity.org
    # Copyright (c) 2002-2004 Ivan Ristic <ivanr@webkreator.com>
    #
    # $Id: modsecurity-manual.xml,v 1.8.2.14 2006/05/15 08:39:07 ivanr Exp $
    #
    # This script is an interface between mod_security and its
    # ability to intercept files being uploaded through the
    # web server, and ClamAV

    # by default use the command-line version of ClamAV,
    # which is slower but more likely to work out of the
    # box
    $CLAMSCAN = "/usr/bin/clamscan";

    # using ClamAV in daemon mode is faster since the
    # anti-virus engine is already running, but you also
    # need to configure file permissions to allow ClamAV,
    # usually running as a user other than the one Apache
    # is running as, to access the files
    # $CLAMSCAN = "/usr/bin/clamdscan";

    if (@ARGV != 1) {
        print "Usage: modsec-clamscan.pl <filename>\n";
        exit;
    }

    my ($FILE) = @ARGV;

    $cmd = "$CLAMSCAN --stdout --disable-summary $FILE";
    $input = `$cmd`;
    $input =~ m/^(.+)/;
    $error_message = $1;

    $output = "0 Unable to parse clamscan output [$1]";

    if ($error_message =~ m/: Empty file\.$/) {
        $output = "1 empty file";
    }
    elsif ($error_message =~ m/: (.+) ERROR$/) {
        $output = "0 clamscan: $1";
    }
    elsif ($error_message =~ m/: (.+) FOUND$/) {
        $output = "0 clamscan: $1";
    }
    elsif ($error_message =~ m/: OK$/) {
        $output = "1 clamscan: OK";
    }

    print "$output\n";

#### Upload memory limit

Apache 1.x does not offer a proper infrastructure for request interception. It is only possible to intercept requests storing them completely in the operating memory. With Apache 1.x there is a choice to analyse `multipart/form-data` (file upload) requests in memory or not analyse them at all (selectively turn `POST` processing off).

With Apache 2.x, however, you can define the amount of memory you want to spend parsing multipart/form-data requests in memory. When a request is larger than the memory you have allowed a temporary file will be used. The default value is 60 KB but the limit can be changed using the `SecUploadInMemoryLimit` directive:

    SecUploadInMemoryLimit 125000

### Server identity masking

One technique that often helps slow down and confuse attackers is the web server identity change. Web servers typically send their identity with every HTTP response in the Server header. Apache is particularly helpful here, not only sending its name and full version by default, but it also allows server modules to append their versions too.

To change the identity of the Apache web server you would have to go into the source code, find where the name "Apache" is hard-coded, change it, and recompile the server. The same effect can be achieved using the `SecServerSignature` directive:

    SecServerSignature "Microsoft-IIS/5.0"

It should be noted that although this works quite well, skilled attackers (and tools) may use other techniques to "fingerprint" the web server. For example, default files, error message, ordering of the outgoing headers, the way the server responds to certain requests and similar - can all give away the true identity. I will look into further enhancing the support for identity masking in the future releases of mod_security.

If you change Apache signature but you are annoyed by the strange message in the error log (some modules are still visible - this only affects the error log, from the outside it still works as expected):

    [Fri Jun 11 04:02:28 2004] [notice] Microsoft-IIS/5.0 mod_ssl/2.8.12 OpenSSL/0.9.6b \
    configured -- resuming normal operations

Then you should re-arrange the modules loading order to allow mod_security to run last, exactly as explained for chrooting.

### Note

In order for this directive to work you must leave/set `ServerTokens` to Full.

When the `SecServerSignature` directive is used to change the public server signature, ModSecurity will start writing the real signature to the error log, to allow you to identify the web server and the modules used.

    [Fri Jun 11 04:02:28 2004] [notice] mod_security/1.9dev1 configured - Apache/2.0.52 \
    (Unix) PHP/4.3.10 proxy_html/2.4

### Chroot support

#### Standard approach

ModSecurity includes support for Apache filesystem isolation, or chrooting. Chrooting is a process of confining an application into a special part of the file system, sometimes called a "jail". Once the chroot (short for “change root”) operation is performed, the application can no longer access what lies outside the jail. Only the root user can escape the jail (in most cases, there are some circumstances when even non-root users can escape too, but only on an improperly configured jail). A vital part of the chrooting process is not allowing anything root related (root processes or root suid binaries) inside the jail. The idea is that if an attacker manages to break in through the web server he won't have much to do because he, too, will be in jail, with no means to escape.

Applications do not have to support chrooting. Any application can be chrooted using the chroot binary. The following line:

    chroot /chroot/apache /usr/local/web/bin/apachectl start

will start Apache but only after replacing the file system with what lies beneath `/chroot/apache`.

Unfortunately, things are not as simple as this. The problem is that applications typically require shared libraries, and various other files and binaries to function properly. So, to make them function you must make copies of required files and make them available inside the jail. This is not an easy task. (I covered the process in detail in my book, Apache Security. The chapter that covers chroot is available for free at http://www.apachesecurity.net).

#### The ModSecurity way

While I was chrooting an Apache the other day I realised that I was bored with the process and I started looking for ways to simplify it. As a result, I built the chrooting functionality into the `mod_security` module itself, making the whole process less complicated. With ModSecurity under your belt, you only need to add one line to the configuration file:

    SecChrootDir /chroot/apache

and your web server will be chrooted successfully.

### Note

The internal chroot functionality provided by ModSecurity works great for simple setups. One example of a simple setup is Apache serving static files only, or running scripts using modules. For more complex setups you should consider building a jail the old-fashioned way.

### Note

The internal chroot feature should be treated as somewhat experimental. Due to the large number of default and third-party modules available for the Apache web server, it is not possible to verify the internal chroot works reliably with all of them. You are advised to think about your option and make your own decision. In particular, if you are using any of the modules that fork in the module initialisation phase (e.g. mod_fastcgi, mod_fcgid, mod_cgid), you are advised to examine each Apache process and observe its current working directory, process root, and the list of open files.

What follows is a list of facts about the internal chroot functionality for you to consider before making the decision:

1. Unlike external chrooting (mentioned previously) ModSecurity chrooting requires no additional files to exist in jail. The chroot call is made after web server initialisation but before forking. Because of this, all shared libraries are already loaded, all web server modules are initialised, and log files are opened. You only need your data files in the jail.

2. To create new processes from within jail your either need to use statically-compiled binaries or place shared libraries in the jail too.

3. With Apache 2.x, the default value for the `AcceptMutex` directive is `pthread`. Sometimes this setting prevents Apache from working when the chroot functionality is used. Set `AcceptMutex` to any other setting to overcome this problem (e.g. `posixsem`). If you configure chroot to leave log files outside the jail, Apache will have file descriptors pointing to files outside the jail. The chroot mechanism was not initially designed for security and some people fill uneasy about this.

4. If your Apache installation uses mod_ssl you will find that it is not possible to leave the logs directory outside the jail when a file-based SSL mutex is used. This is because `mod_ssl` creates a lock file in the logs directory immediately upon startup, but fails when it cannot find it later. This problem can be avoided by using some other mutex type, for example `SSLMutex sem`, or by telling `mod_ssl` to place its file-based mutex in a directory that is inside the jail (using `SSLMutex file://path/to/file`).

5. If you are trying to use the chroot feature with a multithreaded Apache installation you may get the folllowing message "libgcc_s.so.1 must be installed for pthread_cancel to work". Add `LoadFile /lib/libgcc_s.so.1` to your Apache configuration to fix this problem.

6. The files used by Apache for authentication must be inside the jail since these files are opened on every request.

7. Certain modules (e.g. `mod_fastcgi`, `mod_fcgi`, `mod_cgid`) fork in the module initialisation phase. If they fork before chroot takes place they create a process that lives outside jail. In this case ModSecurity must be configured to initialise after most modules but before the modules that fork. This is a manual process with Apache 1.3.x. It is an automated process with Apache 2.x since ModSecurity 1.9.3.

##### Required module ordering for chroot support (Apache 1.x)

### Note

This step should not be needed if you intend to leave the log files inside the jail.

As mentioned above, the chroot call must be performed at a specific moment in Apache initialisation, only after all other modules are initialised. This means that ModSecurity must be the first on the list of modules. To ensure that, you will probably need to make some changes to module ordering, using the following configuration directives:

    ClearModuleList
    AddModule mod_security.c
    AddModule ...
    AddModule ...
    AddModule ...

The first directive clears the list. You must put ModSecurity next, followed by all other modules you intend to use (except http_core.c, which is always automatically added and you do not have to worry about it). You can find out the list of built-in modules by executing the httpd binary with the -l switch:

    ./httpd -l

### Note

If you choose to put the Apache binary and the supporting files outside of jail, you won't be able to use the `apachectl graceful` and `apachectl restart` commands anymore. That would require Apache reaching out of the jail, which is not possible. With Apache 2, even the apachectl stop command may not work.

##### Required module ordering for chroot support (Apache 2.x)

### Note

This step should not be needed if you intend to leave the log files inside the jail.

With Apache 2.x you shouldn't need to manually configure module ordering since Apache 2.x already includes support for module ordering internally. ModSecurity uses this feature to tell Apache 2.x when exactly to call it and chroot works (if you're having problems let me know).

There was a change in how the process is started in Apache2. The httpd binary itself now creates the pid file with the process number. Because of this you will need to put Apache in jail at the same folder as outside the jail. Assuming your Apache outside jail is in `/usr/local/web/apache` and you want jail to be at `/chroot` you must create a folder `/chroot/usr/local/web/apache/logs`.

When started, the Apache will create its pid file there (assuming you haven't changed the position of the pid file in the `httpd.conf` in which case you probably know what you're doing).

##### A step-by-step chroot guide

If you follow this step-by-step guide you won't even have to bother with the module ordering. First install Apache as you normally would. Here I will assume Apache was installed into `/usr/local/apache`. I will also assume the jail will be placed at `/chroot/apache`. It is always a good idea the installation was successful by starting the server and checking it works properly.

    # mkdir -p /chroot/apache/usr/local
    # cd /usr/local
    # mv apache /chroot/apache/usr/local
    # ln -s /chroot/apache/usr/local/apache

Now instruct ModSecurity to perform chroot upon startup:

    SecChrootDir /chroot/apache

And start Apache:

    /usr/local/apache/bin/apachectl startssl

### Note

This procedure describes an approach where the Apache files are left inside the jail after chroot takes place. This is the recommended approach because it works every time, and because it is very easy to switch from a non-chrooted Apache to a chrooted one (simply by commenting the `SecChrootDir` line in the configuration file). It is perfectly possible, however, to create a jail where most of the files are outside. But this is also an option that is more difficult to get right. A good understanding of the chroot mechanism is needed to get it right.

### Note

Since version 1.8, if ModSecurity fails to perform chroot for any reason it will prevent the server from starting. If it fails to detect chroot failure during the configuration phase and then detects it at runtime, it will write a message about that in the error log and exit the child. This may not be pretty but it is better than running without a protection of a chroot jail when you think such protection exists.

#### Performance measurement

In 1.9dev1 I introduced experimental support for performance measurement to the Apache 2 version of ModSecurity. Measuring script performance is sometimes difficult if the clients are on a slow link. Because the response is generated and sent to the client at the same time it is not possible to separate the two. The only way to measure performance is to withhold from sending the response in parts, and only send it when it is generated completely. This is exactly what ModSecurity does anyway (for security purposes) so it makes sense to use it for performance measurement. Three time measurements are performed and data stored the results in Apache notes. All times are given in microseconds relative to the start of request processing:

- `mod_security-time1` - ModSecurity initialisation completed. If the request contains a body the body will have been read by now (provided `POST` scanning is enabled).

- `mod_security-time2` - ModSecurity completes rule processing. Since we try to execute last, just before request is processed by a handler, this time is roughly the time just before processing begins.

- `mod_security-time3` - response has been generated and is about to be sent to the client.

To use these values in a custom log do this (again, this only works with Apache 2):

    CustomLog logs/timer_log "%h %l %u %t \"%r\" %>s %b - %{UNIQUE_ID}e \
    %<{mod_security-time1}n %<{mod_security-time2}n \
    %<{mod_security-time3}n %D"

Each entry in the log will look something like this:

    82.70.94.182 - - [19/Nov/2004:11:33:52 +0000] "GET /cgi-bin/modsec-test.pl HTTP/1.1" \
    200 1418 - 532 1490 13115 14120

In the example above it took 532 microseconds for processing to reach ModSecurity. ModSecurity used 958 microseconds (1490 - 532) to execute the defined rules, the CGI script generated output in 11625 microseconds (13155 - 1490), and Apache took 965 microseconds to send the response to the client.

Prev |  | Next
Actions | Home | Logging

Copyright (C) 2002-2006 Thinking Stone