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:
Going through all the configuration directives will give you a better understanding of how ModSecurity works. Even if there are features that you don’t need immediately, you will learn that they exist and you’ll be able to take advantage of them when the need arises.
By explicitly configuring every single feature, you will foolproof your configuration against incompatible changes to default settings that may happen in future versions of ModSecurity.
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:
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.
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.
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
Directive | Description |
---|---|
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 |
SecRequestBodyLimitAction | Controls 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”.
Location | Owner | Group | 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:
As already discussed, it is root
that owns everything by
default, and we assign ownership to apache
only where that is
necessary.
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.
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.)
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.
As an exception to the proposed layout, you may want to reuse Apache’s log directory for ModSecurity logs. If you don’t, you’ll have the error log separate from the debug log (and the audit log if you choose to use the serial logging format). In a reverse proxy installation in particular, it makes great sense to keep everything integrated and easier to find. There may be other good reasons for breaking convention. For example, if you have more than one hard disk installed and you use the audit logging feature a lot, you may want to split the I/O operations across the disks.
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”.
Table 3.3. Configuration files
Filename | Description |
---|---|
main.conf
| Main configuration file |
rules-first.conf
| Rules that need to run first |
rules.conf
| Your principal rule file |
rules-last.conf
| Rules that need to run last |
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 </IfModule>
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.
When the directive SecStreamInBodyInspection
is
enabled, it will attempt to store the entire raw request body in
STREAM_INPUT_BODY
. In this case, you lose the protection of
SecRequestBodyNoFilesLimit
; the maximum amount of memory
consumed for buffering will be that defined with
SecRequestBodyLimit
.
In blocking mode, ModSecurity will respond with a 413 (Request
Entity Too Large) response status code when a request body limit is reached. This
response code was chosen to mimic what Apache does in similar circumstances. See
SecRequestBodyLimitAction
for more information.
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
To instruct ModSecurity to inspect the response bodies for which the MIME type is
unknown (meaning that it was not specified in the response headers), use the special
string (null)
as a parameter to
SecResponseBodyMimeType
.
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
You may experience permission-related issues in ModSecurity versions 2.5.10 and
2.5.11 (but not in 2.5.12 and better), in which process umask affects the
permissions of newly created files. Thus, if you set
SecUploadFileMode
to 0660
, but the umask
is 022
, the resulting permissions will be 0644
(0660 & ~022
). If you need to change the process umask,
edit the script that you use to control Apache (e.g., apachectl
in my case). For example, assuming that you wish to set the umask to
002
, add umask 002
to the end of the
script.
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. SecAuditLogParts ABDEFHIJKZ
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.
It is possible to write rules that ignore the default policies. If you are using
third-party rule sets and you are not sure how they will behave, consider switching
the entire engine to detection only (using SecRuleEngine
). No
rule will block when you do that, regardless of how it was designed to work.
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.
I am including these rules here because they should be an integral part of every configuration, but you shouldn’t worry if you don’t understand what it is that they do exactly. The rules will be explained in detail later in the book.
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). SecRule REQBODY_PROCESSOR_ERROR "!@eq 0" \ "phase:2,t:none,log,block,msg:'Failed to parse request body: ↩ %{REQBODY_PROCESSOR_ERROR_MSG}'"
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. SecRule MULTIPART_STRICT_ERROR "!@eq 0" \ "phase:2,t:none,log,block,msg:'Multipart request body \ failed strict validation: \ PE %{REQBODY_PROCESSOR_ERROR}, \ BQ %{MULTIPART_BOUNDARY_QUOTED}, \ BW %{MULTIPART_BOUNDARY_WHITESPACE}, \ DB %{MULTIPART_DATA_BEFORE}, \ DA %{MULTIPART_DATA_AFTER}, \ HF %{MULTIPART_HEADER_FOLDING}, \ LF %{MULTIPART_LF_LINE}, \ SM %{MULTIPART_MISSING_SEMICOLON}, \ IQ %{MULTIPART_INVALID_QUOTING}, \ IF %{MULTIPART_INVALID_HEADER_FOLDING}, \ FE %{MULTIPART_FILE_LIMIT_EXCEEDED}'"
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:
SecRule TX:MSC_PCRE_LIMITS_EXCEEDED "@eq 1" \ "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:
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:
SecRule ARGS MY_UNIQUE_TEST_STRING
\
"phase:2,log,deny,status:503"
Restart Apache, using the graceful
restart option if your
server is in production and you don’t want any downtime.
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.
Verify that the message has appeared in both the error log and the debug log, and that the audit log contains the complete transaction.
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.
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.
Finally, and just to be absolutely sure, examine the permissions on all Apache and ModSecurity locations and verify that they are correct.
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.