Now that you have ModSecurity installed 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 most of 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 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’m 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 (with the exception of the logging directives, which are listed in several tables in Chapter 4, Logging).
Table 3.1. Main configuration directives
Directive | Description |
---|---|
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 |
SecUploadDir
| Sets the folder in which intercepted files will be stored |
SecUploadFileLimit
| Set the maximum number of file uploads processed in a multipart POST |
SecUploadKeepFiles
| Controls whether the uploaded files will be kept after the transaction is processed |
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’s difficult to give specific advice. Similarly, different choices may be appropriate in different circumstances. For example, if you’re 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 always prefer to 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 (/usr/local/modsecurity
) with multiple subfolders
underneath. The subfolders that are written to at runtime are all grouped (in
/usr/local/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 also means root
must be the
principal owner of the installation. Because it’s not good practice to stay as
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.
Location | Owner | Group | Permissions |
---|---|---|---|
/usr/local/modsecurity
|
root
|
apache
|
rwxr-x---
|
/usr/local/modsecurity/bin
|
root
|
apache
|
rwxr-x---
|
/usr/local/modsecurity/etc
|
root
|
root
|
rwx------
|
/usr/local/modsecurity/var
|
root
|
apache
|
rwxr-x---
|
/usr/local/modsecurity/var/audit
|
apache
|
root
|
rwx------
|
/usr/local/modsecurity/var/data
|
apache
|
root
|
rwx------
|
/usr/local/modsecurity/var/log
|
root
|
root
|
rwx------
|
/usr/local/modsecurity/var/tmp
|
apache
|
apache
|
rwxr-x---
|
/usr/local/modsecurity/var/upload
|
apache
|
root
|
rwx------
|
I’ve arrived at the desired permission layout through the following requirements:
As already discussed, root
that owns
everything by default, and we assign ownership to apache
only
when necessary.
In two cases (/usr/local/modsecurity
and
/usr/local/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
/usr/local/modsecurity/bin
folder, where you might
store some binaries Apache will need to execute at runtime.
One folder,
/usr/local/modsecurity/var/log
, stands out; it’s the
only folder underneath /usr/local/modsecurity/var
to which
apache
is not allowed to write. That folder contains log
files that are opened by Apache early on while it’s 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
. Anything
else 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’s written to the log files. That can lead to system
compromise.)
A careful observer will notice that I’ve allowed group folder
access to /usr/local/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’ll 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 via ClamAV). To allow the
third user account to access the files created by ModSecurity, 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. The layout depends on what you want to do with ModSecurity. If you plan to run the OWASP ModSecurity Core Rule Set, for example, you’ll follow their setup proposal to a certain extent. Other rule layout conventions have more to do with taste than anything else, but in this section I’ll describe an approach that’s 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.
Table 3.3. Configuration files
Filename | Description |
---|---|
main.conf
| Main configuration file |
rules-first.conf
| Rules that need to run first |
rules.conf
| Principal rule file |
rules-last.conf
| Rules that need to run last |
Your main configuration file (modsecurity.conf
)
thus may contain only the following lines:
Include /usr/local/modsecurity/etc/main.conf Include /usr/local/modsecurity/etc/rules-first.conf Include /usr/local/modsecurity/etc/rules.conf Include /usr/local/modsecurity/etc/rules-last.conf
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 Lua LoadFile /usr/lib/x86_64-linux-gnu/liblua5.3.so # Finally, load ModSecurity LoadModule security2_module modules/mod_security2.so
Now you just need to tell Apache where to find the configuration:
<IfModule mod_security2.c> Include /usr/local/modsecurity/etc/modsecurity.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; it
allows you to deactivate a module simply by commenting out the appropriate
LoadModule
line.
Prior to Apache 2.4, it was necessary to load the libxml2.so
file similar to liblua5.2.so
. However, this is no longer the
case, because the XML library is now linked into the ModSecurity module
directly.
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 can be sure nothing will be blocked:
# Enable ModSecurity, attaching it to every transaction. SecRuleEngine DetectionOnly
You’ll 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’ll 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 can 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 and depends on the HTTP method employed. 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 not only will have access to the content transmitted in request bodies but also will 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; 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 occurs. 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
File uploads generally don’t use RAM (and thus don’t create an opportunity for a
memory-based denial of service attack), which means that it’s safe to allow large
requests as defined by SecRequestBodyLimit
. With all the other
requests, the RAM usage has to be considered, and a lower limit is imperative.
SecRequestBodyNoFilesLimit
is applied in such cases.
When the SecStreamInBodyInspection
directive 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 for more information.
The third directive that addresses 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 up the
bulk of the traffic on most web sites, most of which is just static files that don’t
have any security relevance in most cases. The response MIME type is used to distinguish
interesting responses from those that are not. The
SecResponseBodyMimeType
directive lists the response MIME types
you’re interested in:
# Which response MIME types do you want to look at? You # should adjust this configuration to catch documents # but avoid static files (e.g., images and archives). SecResponseBodyMimeType text/plain text/html
To instruct ModSecurity to inspect 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 for
SecResponseBodyMimeType
.
You can control the size of a response body buffer via 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 for which 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 first glance, 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’s long enough to bypass observation by ModSecurity—and this is true. However, if you have an attacker with full control of output, it’s 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 cases in which 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 # (e.g., when it needs to handle a multipart request # body that's larger than the configured limit). If you don't # specify a location here, your system's default will be used. # It's recommended that you specify a location that's private. SecTmpDir /usr/local/modsecurity/var/tmp/ # The location where ModSecurity will keep its data. This, # too, needs to be a path that other users can't access. # IMPORTANT: The path defined by SecDataDir must reside on # on the same partition as the path defined by SecTmpDir. SecDataDir /usr/local/modsecurity/var/data/
Next, we’ll configure the handling of file uploads. We’ll configure the folder where ModSecurity will store intercepted files, but 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 where you really need it.
# The location where ModSecurity will store intercepted # uploaded files. This location must be private to ModSecurity. SecUploadDir /usr/local/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 # don't allow any other user to access them. You may need to # relax that if you want to interface ModSecurity with an # external program (e.g., an anti-virus program). SecUploadFileMode 0600
You should set 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
There isn’t a limit by default, so setting one in the configuration is
very important. The issue here first is that it’s easy for an attacker to include many
embedded files (hundreds or even thousands) in a single
multipart/form-data
request, but also 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 performance. The debug log will duplicate what you’ll also see in Apache’s error log up to level 3. If the error log is growing fast and has to be rotated quickly, it can be useful to keep the ModSecurity-related messages longer in the debug log. However, if there are a lot of ModSecurity alerts, redundancy will be an issue, and you’ll need to make sure you also rotate the debug log regularly. It’s perfectly okay to run with a debug log level of 0 and to rely on the Apache error log. Any value above 3 is not recommended in production.
# Debug log SecDebugLog /usr/local/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’re 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's really necessary. SecAuditEngine RelevantOnly
In addition, we’ll 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
Alternatively, you can also log client errors in the range of 400–499. This can be useful because affected users often will contact you with support questions. You probably don’t want to log status code 404 (Not Found) in this case, so the complete regular expression to keep an audit log of all erroneous requests—with the exception of 404—is as follows:
# Also log requests that cause an error. SecAuditLogRelevantStatus "^(?:5|4(?!04))"
The audit log separates its records into multiple parts. Each part is
assigned a single letter. You enable the logging of the individual parts by listing them
as parameters of the SecAuditLogParts
directive. 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 installations that will log a lot and it prevents remote logging, but it’s good enough to start with:
# Use a single file for logging. SecAuditLogType Serial SecAuditLog /usr/local/modsecurity/var/log/audit.log
As the final step, we’ll 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 /usr/local/modsecurity/var/audit/
Now that we’re nearing the end of the configuration, you need to decide what you want to happen when a rule matches. We recommend 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’s possible to write rules that ignore the default policies. If
you’re using third-party rulesets and 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 it to you to deal with them. In our default configuration, we’ll have a couple of rules to deal with the situations that ModSecurity can’t deal with on its own: processing errors.
I’m including these rules here because they should be an integral part of every configuration, but you shouldn’t worry if you don’t understand exactly what it is that they do. The mechanics of the rules will be explained in detail later in the book.
There are currently three types of processing errors:
Request and response buffering limits encountered
Parsing errors
PCRE limit 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 can detect the processing errors.
Currently, the only parsing errors that can happen are request body
processor errors. We’ll use two rules to handle such errors. 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" \ "id:2000,phase:2,block,t:none,log,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" \ "id:2001,phase:2,block,t:none,log,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 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 ModSecurity users’ mailing list, noting that you’ve encountered an interesting attacker or a ModSecurity bug.
PCRE limits are set to protect the
server from denial of service attacks via excessive resource consumption in regular
expression calculations. The default limits are very low. Therefore, users can control
the setting of the limits via SecPcreMatchLimit
and
SecPcreMatchLimitRecursion
. The debug log will identify rules
that have exceeded the limits—for example:
[3] Rule 292d670 [id "941140"][file "/usr/local/modsecurity/etc/core-rules/REQUEST-941-APPLICATION-ATTACK-XSS.conf"][line "514"] - Execution error - PCRE limits exceeded (-8): (null).
For now, leave the PCRE limits defaults as they are, but add a rule to warn us when they’re exceeded:
SecRule TX:MSC_PCRE_LIMITS_EXCEEDED "@eq 1" \ "id:9000,phase:5,pass,t:none,log,msg:'PCRE limits exceeded'"
I’ve used phase 5 for the rule, but
if you’re 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, we recommend undertaking 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 string
MY_UNIQUE_TEST_STRING
, responding with a 503 (Service
Unavailable) on a match:
SecRule ARGS "@contains MY_UNIQUE_TEST_STRING
" \
"id:2000,phase:2,deny,status:503,log"
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’re 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’s 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 never be 100% certain that a third-party module was implemented
correctly. For example, it’s possible to write a module that will essentially
hijack a request early on and bypass all other modules, including ModSecurity.
We’re 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’re correct.
In this chapter, we looked at the core configuration options of ModSecurity. Strictly speaking, we could have left many of these options set to their defaults and spent about a tenth of this time on configuration, but I’ve always found it better to explicitly define every setting; with that approach, you end up with the configuration that’s tailored to your needs. In addition, you get to know ModSecurity better, which might prove crucial at some point in the future.
Quite a few more optional configuration directives exist. Most of them are highly advanced or only applicable in rare and special situations. They’re covered in Chapter 15, Directives.
We didn’t pay much attention to logging in this chapter, opting to configure both the debug log and the audit log conservatively. However, 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.