> ModSecurity Handbook: Getting Started: Chapter 3. Configuration


3 Configuration

Now that you have ModSecurity compiled and ready to run, we can proceed to the configuration. This section, with its many subsections, goes through every part of ModSecurity configuration, explicitly configuring every little detail:

In accordance with its philosophy, ModSecurity won’t do anything implicitly. It won’t even run unless you tell it to. There are three reasons for that:

  1. By not doing anything implicitly, we ensure that ModSecurity does only what you tell it to. That not only keeps you in control, but it also makes you think about every feature before you add it to your configuration.

  2. It is impossible to design a default configuration that works in all circumstances. We can give you a framework within which you can work (as I am doing in this section), but you still need to shape your configuration according to your needs.

  3. Security is not free. You pay for it by the increased consumption of RAM, CPU, or the possibility that you may block a legitimate request. Incorrect configuration may cause problems, so we need you to think carefully about what you’re doing.

The remainder of this section explains the proposed default configuration for ModSecurity. You can get a good overview of the default configuration simply by examining the configuration directives supported by ModSecurity, which are listed in Table 3.1, “Main configuration directives” (with the exception of the logging directives, which are listed in several tables in the Chapter 4, Logging).

Table 3.1. Main configuration directives

SecArgumentSeparator Sets the application/x-www-form-urlencoded parameter separator
SecCookieFormat Sets the cookie parser version
SecDataDir Sets the folder for persistent storage
SecRequestBodyAccess Controls request body buffering
SecRequestBodyInMemoryLimit Sets the size of the per-request memory buffer
SecRequestBodyLimit Sets the maximum request body size ModSecurity will accept
SecRequestBodyLimitActionControls what happens once the request body limit is reached
SecRequestBodyNoFilesLimit Sets the maximum request body size, excluding uploaded files
SecResponseBodyAccess Controls response body buffering
SecResponseBodyLimit Specifies the response body buffering limit
SecResponseBodyLimitAction Controls what happens once the response body limit is reached
SecResponseBodyMimeType Specifies a list of response body MIME types to inspect
SecResponseBodyMimeTypesClear Clears the list of response body MIME types
SecRuleEngine Controls the operation of the rule engine
SecTmpDir Sets the folder for temporary files

Your first configuration task is to decide where on the filesystem to put the various bits and pieces that every ModSecurity installation consists of. Installation layout is often a matter of taste, so it is difficult for me to give you advice. Similarly, different choices may be appropriate in different circumstances. For example, if you are adding ModSecurity to a web server and you intend to use it only occasionally, you may not want to use an elaborate folder structure, in which case you’ll probably put the ModSecurity folder underneath Apache’s. When you’re using ModSecurity as part of a dedicated reverse proxy installation, however, a well–thought out structure is something that will save you a lot of time in the long run.

I prefer to always use an elaborate folder layout, because I like things to be neat and tidy, and because the consistency helps me when I am managing multiple ModSecurity installations. I start by creating a dedicated folder for ModSecurity (/opt/modsecurity) with multiple subfolders underneath. The subfolders that are written to at runtime are all grouped (in /opt/modsecurity/var), which makes it easy to relocate them to a different filesystem using a symbolic link. I end up with the following structure:

Getting the permissions right may involve slightly more effort, depending on your circumstances. Most Apache installations bind to privileged ports (e.g., 80 and 443), which means that the web server must be started as root, and that further means that root must be the principal owner of the installation. Because it’s not good practice to stay root at runtime, Apache will switch to a low-privilege account (we’ll assume it’s apache) as soon as it initializes. You’ll find the proposed permissions in Table 3.2, “Folder permissions”.

Table 3.2. Folder permissions

/opt/modsecurity root apache rwxr-x---
/opt/modsecurity/bin root apache rwxr-x---
/opt/modsecurity/etc root root rwx------
/opt/modsecurity/var root apache rwxr-x---
/opt/modsecurity/var/audit apache root rwx------
/opt/modsecurity/var/data apache root rwx------
/opt/modsecurity/var/log root root rwx------
/opt/modsecurity/var/tmp apache apache rwxr-x---
/opt/modsecurity/var/upload apache root rwx------

I’ve arrived at the desired permission layout through the following requirements:

  1. As already discussed, it is root that owns everything by default, and we assign ownership to apache only where that is necessary.

  2. In two cases (/opt/modsecurity and /opt/modsecurity/var), we need to allow apache to access a folder so that it can get to a subfolder; we do this by creating a group, also called apache, of which user apache is the only member. We use the same group for the /opt/modsecurity/bin folder, where you might store some binaries Apache will need to execute at runtime.

  3. One folder, /opt/modsecurity/var/log, stands out, because it is the only folder underneath /opt/modsecurity/var where apache is not allowed to write. That folder contains log files that are opened by Apache early on, while it is still running as root. On any Unix system, you must have only one account with write access to that folder, and it has to be the principal owner. In our case, that must be root. Doing otherwise would create a security hole, whereby the apache user would be able to obtain partial root privileges using symlink trickery. (Essentially, in place of a log file, the apache user creates a symlink to some other root-owned file on the system. When Apache starts it runs as root and opens for writing the system file that the apache user would otherwise be unable to touch. By submitting requests to Apache, one might be able to control exactly what is written to the log files. That can lead to system compromise.)

  4. A careful observer will notice that I’ve allowed group folder access to /opt/modsecurity/var/tmp (which means that any member of the apache group is allowed to read the files in the folder) even though this folder is owned by apache, which already has full access. This is because you will sometimes want to allow ModSecurity to exchange information with a third user account—for example, if you want to scan uploaded files for viruses (usually done using ClamAV). To allow the third user account to access the files created by ModSecurity, you just need to make it a member of the apache group and relax the file permissions using the SecUploadFileMode directive.

If you have anything but a trivial setup, spreading configuration across several files is necessary in order to make maintenance easier. There are several ways to do that, and some have more to do with taste than anything else, but in this section I will describe an approach that is good enough to start with.

Whatever configuration design I use, there is usually one main entry point, typically named modsecurity.conf, which I use as a bridge between Apache and ModSecurity. In my bridge file, I refer to any other ModSecurity files I might have, such as those listed in Table 3.3, “Configuration files”.

Your main configuration file (modsecurity.conf) may thus contain only the following lines:

<IfModule mod_security2.c>
Include /opt/modsecurity/etc/main.conf
Include /opt/modsecurity/etc/rules-first.conf
Include /opt/modsecurity/etc/rules.conf
Include /opt/modsecurity/etc/rules-last.conf

The <IfModule> tag is there to ensure that the ModSecurity configuration files are used only if ModSecurity is active in the web server. This is common practice when configuring any nonessential Apache modules and allows you to deactivate a module simply by commenting out the appropriate LoadModule line.

As the first step, make Apache aware of ModSecurity, adding the needed components. Depending on how you’ve chosen to run ModSecurity, this may translate to adding one or more lines to your configuration file. This is what the lines may look like:

# Load libxml2
LoadFile /usr/lib/libxml2.so
# Load Lua
LoadFile /usr/lib/liblua5.1.so
# Finally, load ModSecurity
LoadModule security2_module modules/mod_security2.so

Now you just need to tell Apache where to find the configuration:

Include /opt/modsecurity/etc/modsecurity.conf

ModSecurity has a master switch—the SecRuleEngine directive—that allows you to quickly turn it on and off. This directive will always come first in every configuration. I generally recommend that you start in detection-only mode, because that way you are sure nothing will be blocked.

# Enable ModSecurity, attaching it to every transaction.
SecRuleEngine DetectionOnly

You will normally want to keep this setting enabled, of course, but there will be cases in which you won’t be exactly sure whether ModSecurity is doing something it shouldn’t be. Whenever that happens, you will want to set it to Off, just for a moment or two, until you perform a request without it running.

The SecRuleEngine directive is context-sensitive (i.e., it works with Apache’s container tags <VirtualHost>, <Location>, and so on), which means that you are able to control exactly where ModSecurity runs. You can use this feature to enable ModSecurity only for some sites, parts of a web site, or even for a single script only. I discuss this feature in detail later.

Requests consist of two parts: the headers part, which is always present, and the body, which is optional. Use the SecRequestBodyAccess directive to tell ModSecurity to look at request bodies:

# Allow ModSecurity to access request bodies. If you don't,
# ModSecurity won't be able to see any POST parameters
# and that's generally not what you want.
SecRequestBodyAccess On

Once this feature is enabled, ModSecurity will not only have access to the content transmitted in request bodies, but it will also completely buffer them. The buffering is essential for reliable attack prevention. With buffering in place, your rules have the opportunity to inspect requests in their entirety, and only after you choose not to block will the requests be allowed through.

The downside of buffering is that, in most cases, it uses RAM for storage, which needs to be taken into account when ModSecurity is running embedded in a web server. There are three directives that control how buffering is done. The first two, SecRequestBodyLimit and SecRequestBodyNoFilesLimit, establish request limits:

# Maximum request body size we will accept for buffering.
# If you support file uploads then the value given on the
# first line has to be as large as the largest file you
# want to accept. The second value refers to the size of
# data, with files excluded. You want to keep that value
# as low as practical.
SecRequestBodyLimit 1310720
SecRequestBodyNoFilesLimit 131072

In the versions prior to 2.5, ModSecurity supported only SecRequestBodyLimit (which establishes an absolute limit on a request body), but that directive turned out to be impractical in combination with file uploads. File uploads generally do not use RAM (and thus do not create an opportunity for a denial of service attack), which means that it is safe to allow such large requests. Unfortunately, doing so also meant allowing large requests that are not file uploads, defying the purpose for which the directive was introduced in the first place. The second directive, SecRequestBodyNoFilesLimit, which was introduced in ModSecurity 2.5, calculates request body sizes slightly differently, ignoring the space taken up by files. In practice, this means that the maximum allowed request body size will be that specified in the SecRequestBodyNoFilesLimit directive, with the exception of file uploads, where the setting in SecRequestBodyLimit takes precedence.

The third directive that deals with buffering, SecRequestBodyInMemoryLimit, controls how much of a request body will be stored in RAM, but it only works with file upload (multipart/form-data) requests:

# Store up to 128 KB of request body data in memory. When
# the multipart parser reaches this limit, it will start
# using your hard disk for storage. That is generally slow,
# but unavoidable.
SecRequestBodyInMemoryLimit 131072

The request bodies that fit within the limit configured with SecRequestBodyInMemoryLimit will be stored in RAM. The request bodies that are larger will be streamed to disk. This directive allows you to trade performance (storing request bodies in RAM is fast) for size (the storage capacity of your hard disk is much bigger than that of your RAM).

Similarly to requests, responses consist of headers and a body. Unlike requests, however, most responses have bodies. Use the SecResponseBodyAccess directive to tell ModSecurity to observe (and buffer) response bodies:

# Allow ModSecurity to access response bodies. We leave
# this disabled because most deployments want to focus on
# the incoming threats, and leaving this off reduces
# memory consumption.
SecResponseBodyAccess Off

I prefer to start with this setting disabled, because many deployments don’t care to look at what leaves their web servers. Keeping this feature disabled means ModSecurity will use less RAM and less CPU. If you care about output, however, just change the directive setting to On.

There is a complication with response bodies, because you generally only want to look at the bodies of some of the responses. Response bodies make the bulk of the traffic on most web sites, and the majority of that are just static files that don’t have any security relevance in most cases. The response MIME type is used to distinguish the interesting responses from the ones that are not. The SecResponseBodyMimeType directive lists the response MIME types you are interested in.

# Which response MIME types do you want to look at? You
# should adjust the configuration below to catch documents
# but avoid static files (e.g., images and archives).
SecResponseBodyMimeType text/plain text/html

You can control the size of a response body buffer using the SecResponseBodyLimit directive:

# Buffer response bodies of up to 512 KB in length.
SecResponseBodyLimit 524288

The problem with limiting the size of a response body buffer is that it breaks sites whose pages are longer than the limit. In ModSecurity 2.5, we introduced the SecResponseBodyLimitAction directive, which allows ModSecurity users to choose what happens when the limit is reached:

# What happens when we encounter a response body larger
# than the configured limit? By default, we process what
# we have and let the rest through.
SecResponseBodyLimitAction ProcessPartial

If the setting is Reject, the response will be discarded and the transaction interrupted with a 500 (Internal Server Error) response code. If the setting is ProcessPartial, which I recommend, ModSecurity will process what it has in the buffer and allow the rest through.

At the first thought, it may seem that allowing the processing of partial response bodies creates a security issue. For the attacker who controls output, it seems easy to create a response that is long enough to bypass observation by ModSecurity. This is true. However, if you have an attacker with full control of output, it is impossible for any type of monitoring to work reliably. For example, such an attacker could encrypt output, in which case it will be opaque to ModSecurity. Response body monitoring works best to detect information leakage, configuration errors, traces of attacks (successful or not), and data leakage in the cases when an attacker does not have full control of output.

Other than that, response monitoring is most useful when it comes to preventing the data leakage that comes from low-level error messages (e.g., database problems). Because such messages typically appear near the beginning of a page, the ProcessPartial setting will work just as well to catch them.

We’ve made the decisions regarding filesystem locations already, so all we need to do now is translate them into configuration. The following two directives tell ModSecurity where to create temporary files (SecTmpDir) and where to store persistent data (SecDataDir):

# The location where ModSecurity will store temporary files
# (for example, when it needs to handle a multipart request
# body that is larger than the configured limit). If you don't
# specify a location here your system's default will be used.
# It is recommended that you specify a location that's private.
SecTmpDir /opt/modsecurity/var/tmp/

# The location where ModSecurity will keep its data. This,
# too, needs to be a path that other users can't access.
SecDataDir /opt/modsecurity/var/data/

Next, we configure the handling of file uploads. We configure the folder where ModSecurity will store intercepted files, but we keep this functionality disabled for now. File upload interception slows down ModSecurity and can potentially consume a lot of disk space, so you’ll want to enable this functionality only in the places where you really need it.

# The location where ModSecurity will store intercepted
# uploaded files. This location must be private to ModSecurity.
SecUploadDir /opt/modsecurity/var/upload/

# By default, do not intercept (nor store) uploaded files.
SecUploadKeepFiles Off

For now, we also assume that you will not be using external scripts to inspect uploaded files. That allows us to keep the file permissions more secure, by allowing access only to the apache user:

# Uploaded files are by default created with permissions that
# do not allow any other user to access them. You may need to
# relax that if you want to interface ModSecurity to an
# external program (e.g., an anti-virus).
SecUploadFileMode 0600

If you are using ModSecurity 2.5.12 or later, you should lower the maximum number of files that ModSecurity will handle in a request:

# Limit the number of files we are willing
# to handle in any one request.
SecUploadFileLimit 32

The default value is 100, but that’s usually too much. The issue here is that it is very easy for an attacker to include many embedded files in a single multipart/form-data request (for example, hundreds and even thousands), but also that you don’t want ModSecurity to create that many files on the filesystem (which happens only if the storage or validation of uploaded files is required), because it would create a denial of service situation.

Debug logging is very useful for troubleshooting, but in production you want to keep it at minimum, because too much logging will affect the performance. The recommended debug log level for production is 3, which will duplicate in the debug log what you will also see in Apache’s error log. This is handy, because the error log will grow at a faster rate and may be rotated. A copy of the ModSecurity messages in the debug log means that you always have all the data you need.

# Debug log
SecDebugLog /opt/modsecurity/var/log/debug.log
SecDebugLogLevel 3

In ModSecurity terminology, “audit logging” refers to the ability to record complete transaction data. For a typical transaction without a request body, this translates to roughly 1 KB. Multiply that by the number of requests you are receiving daily and you’ll soon realize that you want to keep this type of logging to an absolute minimum.

Our default configuration will use audit logging only for the transactions that are relevant, which means those that have had an error or a warning reported against them. Other possible values for SecAuditEngine are On (log everything) and Off (log nothing).

# Log only what is really necessary.
SecAuditEngine RelevantOnly

In addition, we will also log the transactions with response status codes that indicate a server error (500–599). You should never see such transactions on an error-free server. The extra data logged by ModSecurity may help you uncover security issues, or problems of some other type.

# Also log requests that cause a server error.
SecAuditLogRelevantStatus ^5

By default, we log all transaction data except response bodies. This assumes that you will seldom log (as it should be), because response bodies can take up a lot of space.

# Log everything we know about a transaction.

Using the same assumption, we choose to use a single file to store all the recorded information. This is not adequate for the installations that will log a lot and prevents remote logging, but it is good enough to start with:

# Use a single file for logging.
SecAuditLogType Serial
SecAuditLog /opt/modsecurity/var/log/audit.log

As the final step, we will configure the path that will be used in the more scalable audit logging scheme, called concurrent logging, even though you won’t need to use it just yet:

# Specify the path for concurrent audit logging.
SecAuditLogStorageDir /opt/modsecurity/var/audit/

The directives covered in this section are seldom needed, but having them will allow us to achieve complete coverage of the ModSecurity configuration options. You’ll also be aware that they exist and will be able to use them in the rare cases where they are needed.

The SecArgumentSeparator directive allows you to change the parameter separator used for the application/x-www-form-urlencoded encoding, which is used to transport all GET parameters and most POST parameters.

SecArgumentSeparator &

Virtually all applications use an ampersand for this purpose, but some may not. The HTML 4.01 specification recommends that applications support the use of semicolons as separators (see section B.2.2 Ampersands in URI attribute values) for convenience. In PHP, for example, it is possible to use any character as a separator.

The SecCookieFormat directive selects one of the two cookie parsers available in ModSecurity. Virtually all applications use Netscape-style cookies (sometimes also known as version 0 cookies), so there will be little reason to change this setting:

SecCookieFormat 0

As we’re nearing the end of the configuration, you need to decide what you want to happen when a rule matches. It is recommended that you start without blocking, because that will allow you to monitor the operation of your installation over a period of time and ensure that legitimate traffic is not being marked as suspicious:

SecDefaultAction "phase:1,log,auditlog,pass"

This default policy will work for all rules that follow it in the same configuration context.

As you may recall from our earlier discussion, ModSecurity avoids making decisions for you. It will detect problems as they occur, but it will generally leave to you to deal with them. In our default configuration, we will have a couple of rules to deal with the situations that ModSecurity can’t deal with on its own—processing errors.

There are currently three types of processing errors:

Normally, you don’t need to be too concerned about encountering buffer limits, because they often occur during normal operation. If you do want to take them into account when making decisions, you can use the INBOUND_DATA_ERROR and OUTBOUND_DATA_ERROR variables for request and response buffering, respectively.

ModSecurity parsers are designed to be as permissive as possible without compromising security. They will raise flags when they fail, but also when they encounter something suspicious. By checking the flags in your rules you detect the processing errors.

Currently the only parsing errors that can happen are the request body processor errors. We will use two rules to handle those.

The first rule will examine the REQBODY_PROCESSOR_ERROR flag for errors. This flag will be raised whenever a request body parsing error occurs, regardless of which parser was used for parsing:

# Verify that we've correctly processed the request body.
# As a rule of thumb, when failing to process a request body
# you should reject the request (when deployed in blocking mode)
# or log a high-severity alert (when deployed in detection-only mode).
    "phase:2,t:none,log,block,msg:'Failed to parse request body: ↩

The second rule is specific to the multipart/form-data parser, which is used to handle file uploads. If it detects a problem, it produces an error message detailing the flaws:

# By default be strict with what you accept in the multipart/form-data
# request body. If the rule below proves to be too strict for your
# environment consider changing it to detection-only. You are encouraged
# _not_ to remove it altogether.
"phase:2,t:none,log,block,msg:'Multipart request body \
failed strict validation: \

Errors specific to the multipart parsers should never occur unless an attacker genuinely tries to bypass ModSecurity by manipulating the request body payload. Some versions of ModSecurity did have false positives in this area, but the most recent version should be false-positive-free. If you do encounter such a problem, feel free to post it to the mod-security-users mailing list. It will mean that you’ve encountered an interesting attacker or a ModSecurity bug.

PCRE limit errors are available starting with ModSecurity version 2.5.12, when the way PCRE is used was changed to significantly lower the match and recursion limits. SecPcreMatchLimit and SecPcreMatchLimitRecursion were also added to allow users to control these settings. The lower the PCRE limits, the more difficult it is to subvert PCRE and the regular expressions to commit denial of service attacks. As of ModSecurity version 2.5.13, the debug log will identify rules that have exceeded the limits. For example:

[3] Rule 9905c50 [id "-"][file "/home/ivanr/apache/conf/httpd.conf"][line "525"] - ↩
Execution error - PCRE limits exceeded (-8): (null). 

We will leave the PCRE limits defaults as they are, but add a rule to warn us when they are exceeded:

    "phase:5,t:none,log,pass,msg:'PCRE limits exceeded'"

I’ve used phase 5 for the rule, but if you are really paranoid and think that exceeding PCRE limits is grounds for blocking, switch to phase 2 (and change pass to something else).

After you’re done installing and configuring ModSecurity, it is recommended that you undertake a short exercise to ensure everything is in order:

  1. Add a simple blocking rule to detect something in a parameter. For example, the following rule will inspect all parameters for the word MY_UNIQUE_TEST_STRING, responding with a 503 (Service Unavailable) on a match:

  2. Restart Apache, using the graceful restart option if your server is in production and you don’t want any downtime.

  3. Send a GET request, using your browser, to the ModSecurity-protected server, including the “attack payload” in a parameter (i.e., http://www.example.com/?test=MY_UNIQUE_TEST_STRING). ModSecurity should block the request.

  4. Verify that the message has appeared in both the error log and the debug log, and that the audit log contains the complete transaction.

  5. Submit a POST request that triggers the test rule. With this request, you are testing whether ModSecurity will see the request body, and whether it will be able to pass the data in it to your backend after inspection. For this test, in particular, it is important that you’re testing with the actual application you want to protect. Only doing so will exercise the entire stack of components that make the application. This test is important because of the way Apache modules are written (very little documentation, so module authors generally employ any approach that “works” for them)—you can generally never be 100% certain that a third-party module was implemented correctly. For example, it is possible to write a module that will essentially hijack a request early on and bypass all the other modules, including ModSecurity. We are doing this test simply because we don’t want to leave anything to chance.

  6. If you want to be really pedantic (I have been, on many occasion—you can never be too sure), you may want to consider writing a special test script for your application, which will somehow record the fact that it has been invoked (mine usually writes to a file in /tmp). By sending a request that includes an attack—which will be intercepted by ModSecurity—and verifying that the script has not been invoked, you can be completely sure that blocking works as intended.

  7. Remove the test rule and restart Apache again.

  8. Finally, and just to be absolutely sure, examine the permissions on all Apache and ModSecurity locations and verify that they are correct.

You’re done!

In this chapter, we took the time to look at every configuration option of ModSecurity. Strictly speaking, we could have left many of the options at their defaults and spent about a tenth of this time on configuration. But I’ve always found it better to explicitly define every setting, because with that approach, you end up with the configuration that’s tailored to your needs. In addition to that, you get to know ModSecurity better, which might prove crucial at some point in the future.

We didn’t pay much attention to logging in this chapter, opting to configure both the debug log and the audit log very conservatively. But there’s a wealth of logging options in ModSecurity. In the next chapter, I’ll discuss logging in detail, and conclude with the configuration topics.