ModSecurity

Actions

There are several types of actions:

Specifying actions

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!'"

Note

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).

Note

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.

Per-rule actions

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:

  1. Only one primary action is allowed per action list. A per-rule primary action will override the primary action in the default list.

  2. The actions specified in the per-rule configuration will override the equivalent actions in the default action list.

  3. When the restricted mode (see SecFilterActionsRestricted) is enabled only the meta-data actions can appear in the per-rule action list.

  4. Rules can be merged at configuration-time (preferred, intuitive), or at run-time (not so intuitive). Read on to learn about the differences.

SecFilterSignatureAction

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.

Note

The value of the SecFilterSignatureAction directive will not be inherited in child contexts.

Restricting what can appear in the per-rule action list

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.

Built-in actions

pass

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"

allow

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

deny

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.

status

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.

redirect

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.

proxy

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.

exec

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.

Note

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.

Note

The script you execute must write something (anything) to stdout. If it doesn't ModSecurity will assume execution didn't work.

log

Log filter match to the Apache error log.

nolog

Do not log the filter match. This will also prevent the audit logging from taking place.

skipnext

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

chain

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

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.

Note

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 .

auditlog

Log the transaction information to the audit log.

noauditlog

Do not log transaction information to the audit log.

id, rev, msg, severity

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

Note

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.

mandatory

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.

Note

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

setenv, setnote

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

Request headers added by mod_security

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

Logging the request body

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 

Note

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.

Handling rule matches using ErrorDocument

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).

Making ModSecurity talk to your firewall

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.