There are several types of actions:
A primary action will make a decision whether to continue with
the request or not. There can exist only one primary action. If you
put several primary actions in the parameter, the last action to be
seen will be executed. Primary actions are deny
,
pass
, and redirect
.
Secondary actions will be performed on a filter match
independently on the decision made by primary actions. There can be
any number of secondary actions. For example, exec
is one secondary action.
Flow actions can change the flow of rules, causing the filtering
engine to jump to another rule, or to skip one or several rules. Flow
actions are chain
and
skip
.
Parameters are not really actions, but a method of attaching
parameters to filters. Some of this parameters can be used by real
actions. For example status
supplies the response
code to the primary action deny
.
There are three places where you can put actions. One is the
SecFilterDefaultAction
directive, where you define
actions you want executed for the rules that follow the
directive:
SecFilterDefaultAction "deny,log,status:500"
This example defines an action list that consists of three actions. Commas are used to separate actions in a list. The first two actions consist of a single word. But the third action requires a parameter. Use double colon to separate the parameter from the action name. Action parameters must not contain whitespace unless you surround them in single quotes (escape a single quote in the parameter with a backslash):
SecFilterDefaultAction "deny,log,status:'Hello World!'"
As of 1.8.6, if you specify a non-fatal default action (such as
log,pass
) then it will be ignored during the
initialisation phase. The initialisation phase is designed to gather
information about the request, allowing non-fatal actions would cause
some pieces of the request to be missing (for internal processing in
ModSecurity). Therefore if you want ModSecurity to operate in a
"detect-only" mode you should disable all implicit validations (check
URL encoding, Unicode, cookie format, byte range).
Meta-data actions (id
,
rev
, msg
,
severity
) and the actions that control the flow of
rules (skip
/skipnext
,
chain
) cannot appear in the
SecFilterDefaultAction
directive.
You can also specify per-filter actions. Both filtering directives
(SecFilter
and SecFilterSelective
)
accept a set of actions as an optional parameter. Per-rule actions are
merged with the actions specified in the most recent
SecFilterSignatureAction
directive (the default value
is log,deny,status:403
). The following rules apply to
the merging process:
Only one primary action is allowed per action list. A per-rule primary action will override the primary action in the default list.
The actions specified in the per-rule configuration will override the equivalent actions in the default action list.
When the restricted mode (see
SecFilterActionsRestricted
) is enabled only the
meta-data actions can appear in the per-rule action list.
Rules can be merged at configuration-time (preferred, intuitive), or at run-time (not so intuitive). Read on to learn about the differences.
The SecFilterSignatureAction
directive,
available since 1.9RC1, makes it easier to maintain rule sets. Prior
to 1.9RC1, if one wanted to use per-rule action lists, every action
list had to be complete, e.g. specify a primary action, status codes
etc. This made it very difficult to separate the rules (the logic to
detect attacks) from configuration policy. The
SecFilterSignatureAction
directive can appear many
times within a single configuration context and it applies to the
rules that immediatelly follow it. Also note that, for consistency,
the rules that do not contain custom actions will also inherit the
action list from this directive. For example:
SecFilterDefaultAction log,deny,status:500 # The rule below will respond with actions # specified in the context it is executed in. # You should note that the context a rule is # executed in is not necessarily the context # that rule was created in. Through inheritance # one rule can be executed in many different # contexts. SecFilter 000 # Warning rules SecFilterSignatureAction log,pass SecFilter 111 id:1 SecFilter 222 id:2 # Error rules SecFilterSignatureAction log,deny,status:403 SecFilter 333 id:3 SecFilter 444 id:4 # Rule below, too, will reject with status 403 SecFilter 555
When used together with
SecFilterActionsRestricted
, this directive makes it
easier to include third-party rule sets into the configuration.
The value of the SecFilterSignatureAction
directive will not be inherited in child contexts.
Sometimes, when you want to include third-party rules in your configuration, you may want to appear what actions will be allowed to appear in them. You can do this with the help of the SecFilterActionsRestricted directive:
SecFilterSignatureAction log,deny,status:403 SecFilterActionsRestricted On Include conf/third-party-rules.conf
The only actions allowed in the per-rule configuration when the
restricted mode is enabled are the meta-data rules
id
, msg
, rev
,
and severity
. Other rules will be silently
ignored.
Allow request to continue on filter match. This action is useful when you want to log a match but otherwise do not want to take action.
SecFilter KEYWORD "log,pass"
This is a stronger version of the previous filter. After this action is performed the request will be allowed through and no other filters will be tried:
# stop filter processing for request coming from # the administrator's workstation SecFilterSelective REMOTE_ADDR "^192\.168\.2\.99$" allow
Interrupt request processing on a filter match. Unless the
status action is used too, ModSecurity will immediately return a HTTP
500 error code. If a request is denied the header
mod_security-action
will be added to the list of
request headers. This header will contain the status code used.
Use the supplied HTTP status code when request is denied. The following rule:
SecFilter KEYWORD "deny,status:404"
will return a "Page not found" response when triggered. The
Apache ErrorDocument
directive will be triggered if
present in the configuration. Therefore if you have previously defined
a custom error page for a given status then it will be executed and
its output presented to the user.
On filter match redirect the user to the given URL. For example:
SecFilter KEYWORD "redirect:http://www.modsecurity.org"
This configuration directive will always override HTTP status code, or the deny keyword. The URL must not contain a comma.
On filter match rewrite the request through the internal reverse proxy:
SecFilter KEYWORD "proxy:http://www.example.com"
For this action to work mod_proxy
must be
installed.
Execute a binary on filter match. Full path to the binary is required:
SecFilter KEYWORD "exec:/home/ivanr/report-attack.pl"
This directive does not effect a primary action if it exists. This action will always call script with no parameters, but providing all information in the environment. All the usual CGI environment variables will be there.
You can have one binary executed per filter match. Execution
will add the header mod_security-executed
to the
list of request headers.
You should be aware that forking a threaded process results in all threads being replicated in the new process. Forking can therefore incur larger overhead in multithreaded operation.
The script you execute must write something (anything) to
stdout
. If it doesn't ModSecurity will assume
execution didn't work.
This action allows you to skip over one or more rules. You will use this action when you establish that there is no need to perform some tests on a particular request. By default, the action will skip over the next rule. It can jump any number of rules provided you supply the optional parameter:
SecFilterSelective ARG_p value1 skipnext:2 SecFilterSelective ARG_p value2 SecFilterSelective ARG_p value3
Rule chaining allows you to chain several rules into a bigger test. Only the last rule in the chain will affect the request but in order to reach it, all rules before it must be matched too. Here is an example of how you might use this feature.
I wanted to restrict the administration account to log in only from a certain IP address. However, the administration login panel was shared with other users and I couldn't use the standard Apache features for this. So I used these two rules:
SecFilterSelective ARG_username admin chain SecFilterSelective REMOTE_ADDR "!^YOUR_IP_ADDRESS_HERE$"
The first rule matches only if there exists a parameter username and its value is admin. Only then will the second rule be executed and it will try to match the remote address of the request to the single IP address. If there is no match (note the exclamation mark at the beginning) the request is rejected.
Pause for the specified amount of milliseconds before responding to a request. This is useful to slow down or completely confuse some web scanners. Some scanners will give up if the pause is too long.
Be careful with this option as it comes at a cost. Every web server installation is configured with a limit, the maximal number of requests that may be served at any given time. Using a long delay time with this option may create a "voluntary" denial of service attack if the vulnerability scanner is executing requests in parallel (therefore many .
These four actions all accept one parameter each, and then reproduce the parameters in every log message emitted by ModSecurity. The idea is to be able to classify problems and put more information in the error logs.
id
- unique rule ID
rev
- rule revision; if missing assumed
to be "1"; whenever a rule is changed the revision value must be
incremented
msg
- a text message that will appear in
the error log
severity
- an integer value or a name, as
defined by syslog
. Publishers are advised to
only use the following levels: 2 (high severity), 3 (medium
severity), 4 (low severity) and 5 (normal but significant). Levels
0-1 and 5-7 should only be used by the end users for their own
purposes.
0 EMERGENCY
- system is
unusable
1 ALERT
- action must be taken
immediately
2 CRITICAL
- critical
conditions
3 ERROR
- error conditions
4 WARNING
- warning conditions
5 NOTICE
- normal but significant
conditions
6 INFO
- informational
7 DEBUG
- debug-level messages
These actions only be used on a standalone rule, or on a rule that is starting a chain.
Although the id
action can contain any text,
it is recommended to only use integers. There is no guarantee that, at
some point in future, we will start to accept only integers as valid
rule IDs. Unless you intend to publish rules to the public you should
use the local range: 1-99,999. These are the reserved ranges:
1 – 99999; reserved for your internal needs, use as you see fit but don't publish them to others
100,000-199,999; reserved for internal use of the engine, to assign to rules that do not have explicit IDs
200,000-299,999; reserved for rules published at
modsecurity.org
300,000-399,999; reserved for rules published at
gotroot.com
400,000 and above; unreserved range.
Contact Ivan Ristic to reserve a range.
You can use this action to mark a rule, or a chain or rules, for mandatory inheritance in subcontexts. Read the section on filter inheritance for more information.
Action id can only used on a standalone rule, or on a rule that is starting a chain.
For example:
SecFilter 111 mandatory
or
SecFilter 111 mandatory,chain SecFilter 222
These two actions will set or unset a named environment variable or an Apache. There are three formats you can use.
Choose the name and the value
SecFilter KEYWORD setenv:name=value
Choose just the name, a value "1" will be assumed:
SecFilter KEYWORD setenv:name
Delete an existing variable / note by placing an exclamation mark before the variable name:
SecFilter KEYWORD setenv:!name
Wherever possible, ModSecurity will add information to the request
headers, thus allowing your scripts to find and use them. Obviously, you
will have to configure ModSecurity not to reject requests in order for
your scripts to be executed at all. At a first glance it may be strange
that I'm using the request headers for this purpose instead of, for
example, environment variables. Although environment variables would be
more elegant, input headers are always visible to scripts executed using
an ErrorDocument
directive (see below) while
environment variables are not.
This is the list of headers added:
mod_security-executed
; with the path to the
binary executed
mod_security-action
; with the status code
returned
mod_security-message
; the message about the
problem detected, the same as the message added to the error
log
ModSecurty will export a request body through the mod_security-body note. You can use this for logging:
LogFormat "%h %l %u %t \"%r\" %>s %{mod_security-body}n
If the request is of multipart/request-data type (file upload)
the real request body will be replaced with a simulated
application/x-www-form-urlencoded
content.
If your configuration returns a HTTP status code 500, and you
configure Apache to execute a custom script whenever this code occurs
(for example: ErrorDocument 500 /error500.php
) you
will be able to use your favourite scripting engine to respond to
errors. The information on the error will be in the environment
variables REDIRECT_*
and
HTTP_MOD_SECURITY_*
(as described here: http://httpd.apache.org/docs-2.0/custom-error.html).
In some cases, after detecting a particularly dangerous attack or a series of attacks you will want to prevent further attacks coming from the same source. You can do this by modifying the firewall to reject all traffic coming from a particular IP address (I have written a helper script that works with iptables, download it from here: http://www.apachesecurity.net).
This method can be very dangerous since it can result in a denial of service (DOS) attack. For example, an attacker can use a proxy to launch attacks. Rejecting all requests from a proxy server can be very dangerous since all legitimate users will be affected too.
Since most proxies send information describing the original client
(some information on this is available here http://www.webkreator.com/cms/view.php/1685.html,
under the "Stop hijacking
" header), we can try to be
smart and find the real IP address. While this can work, consider the
following scenario:
The attacker is accessing the application directly but is pretending to be a proxy server, citing a random (or valid) IP address as the real source IP address. If we start rejecting requests based on that deducted information, the attacker will simply change the IP address and continue. As a result we might have banned legitimate users while the attacker is still free searching for application holes.
Therefore this method can be useful only if you do not allow access to the application through proxies, or allow access only through proxies that are well known and, more importantly, trusted.
If you still want to ban requests based on IP address (in spite of all our warnings), you will need to write a small script that will executed on a filter match. The script should extract the IP address of the attacker from environment variables, and then make a call to iptables or ipchains to ban the IP address. We will include a sample script doing this with a future version of mod_security.