ModSecurity

Configuration

ModSecurity configuration directives are added to your configuration file (typically httpd.conf) directly. When it is not always certain whether module will be enabled or disabled on web server start it is customary to enclose its configuration directives in a <IfModule> container tag. This allows Apache to ignore the configuration directives when the module is not active.

<IfModule mod_security.c>
    # mod_security configuration directives
    # ...
</IfModule>

Since Apache allows configuration data to exist in more than one file it is possible to group ModSecurity configuration directives in a single file (e.g. modsecurity.conf) and include it from httpd.conf with the Include directive:

Include conf/modsecurity.conf

Turning filtering on and off

The filtering engine is disabled by default. To start monitoring requests add the following to your configuration file:

SecFilterEngine On

Supported parameter values for this parameter are:

  • On – analyse every request

  • Off – do nothing

  • DynamicOnly – analyse only requests generated dynamically at runtime. Using this option will prevent your web server from using the precious CPU cycles to check requests for static files. To understand how ModSecurity decides what is a dynamically generated request read the section "Choosing what to log".

POST scanning

Request body payload (or POST payload) scanning is disabled by default. To use it, you need to turn it on:

SecFilterScanPOST On

mod_security supports two encoding types for the request body:

  • application/x-www-form-urlencoded - used to transfer form data

  • multipart/form-data – used for file transfers

Other encodings are not used by most web applications. To make sure that only requests with these two encoding types are accepted by the web server, add the following line to your configuration file:

SecFilterSelective HTTP_Content-Type \
"!(^$|^application/x-www-form-urlencoded$|^multipart/form-data;)"

Turning buffering off dynamically

It is possible to turn POST payload scanning off on per-request basis. If ModSecurity sees that an environment variable MODSEC_NOPOSTBUFFERING is defined it will not perform POST payload buffering. For example, to turn POST payload buffering off for file uploads use the following:

SetEnvIfNoCase Content-Type \
"^multipart/form-data;" "MODSEC_NOPOSTBUFFERING=Do not buffer file uploads"

The value assigned to the MODSEC_NOPOSTBUFFERING variable will be written to the debug log, so you can put in there something that will tell you why was buffering turned off.

Controlling ModSecurity dynamically

It is also possible to enable or disable ModSecurity on the per-request basis. This is done via the MODSEC_ENABLE environment variable, in combination with the SetEnvIf and SetEnvIfNoCase directives. If MODSEC_ENABLE is not set the configuration specified with SecFilterEngine will be used. If MODSEC_ENABLE is set the value of SecFilterEngine will be ignored. To values for MODSEC_ENABLE are the same as for the SecFilterEngine directive: On, Off, or DynamicOnly.

Chunked transfer encoding

The HTTP protocol supports a method of request transfer where the size of the payload is not known in advance. The body of the request is delivered in chunks. Hence the name chunked transfer encoding. ModSecurity does not support chunked requests at this time; when a request is made with chunked encoding it will ignore the body of the request. As far as I am aware no browser uses chunked encoding to send requests. Although Apache does support this encoding for some operations most modules (e.g. the PHP module with Apache 1.3.x) don't.

Left unattended this may present an opportunity for an attacker to sneak malicious payload. Add the following line to your configuration to prevent attackers to exploit this weakness:

SecFilterSelective HTTP_Transfer-Encoding "!^$"

This will not affect your ability to send responses using the chunked transfer encoding.

Default action list

Whenever a rule is matched against a request, one or more actions are performed. Individual filters can each have their own actions but it is easier to define a default set of actions for all filters. (You can always have per-rule actions if you want.) You define default actions with the configuration directive SecFilterDefaultAction. For example, the following will configure the engine to log each rule match, and reject the request with status code 404:

SecFilterDefaultAction "deny,log,status:404"

The SecFilterDefaultAction directive accepts only one parameter, a comma-separated list of actions separated. The actions you specify here will be performed on every filter match, except for rules that have their own action lists.

Note

As of 1.8.6, if you specify a non-fatal default action list (a list that will not cause the request to be rejected, for example log,pass) such action list 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. Since this information is required for internal processing such actions cannot be allowed. If you want ModSecurity to operate in a "detect-only" mode you need to disable all implicit validations (URL encoding validation, Unicode encoding validation, cookie format validation, and byte range restrictions).

Note

Some actions cannot appear in the default list. These are: id, rev, skipnext, chain, chained.

Implicit validation

As of 1.8.6 implicit request validation (if configured) will be performed only at the beginning of request processing. Implicit validation consists of the checks of the request line, and the headers.

Note

As of 1.9dev4 Unicode encoding validation is not applied to the contents of the Referer header when part of the initial implicit request validation. This is because this header often contains information from other web sites, and their encoding usually differs from the encoding used on the protected web site.

Filter inheritance

Filters defined in parent folders are normally inherited by nested Apache configuration contexts. This is behaviour is acceptable (and required) in most cases, but not all the time. Sometimes you need to relax checks in some part of the site. By using the SecFilterInheritance directive:

SecFilterInheritance Off

you can instruct ModSecurity to disregard parent filters so that you can start with rules from the scratch. This directive affects rules only. The configuration is always inherited from the parent context but you can override it as you are pleased using the appropriate configuration directives.

Note

Configuration and rule inheritance is always enabled by default. If you have a configuration context beneath one that has had inheritance disabled you will have to explicitly disable inheritance again if that is what you need.

When you choose not to inherit the rules from the parent context you can either write new rules for the new context, or simply use the Include directive to include the same rules into many different contexts.

Sometimes only a small change to the rule set is required in the child context. In such cases you may choose to use the selective inheritance option. You can do this with the help of the following two directives:

  • SecFilterImport – import a single rule from the parent context. This directive is useful when you want to start from scratch in the child context and only import selected rules from the parent context.

  • SecFilterRemove – remove a rule from the current context. This directive is useful when you want to start with the same rule set as in the parent context, removing selected rules only.

The SecFilterImport and SecFilterRemove directives both accept a list rule IDs. The target rules must have IDs associated with them (this is done using the id action). The directives will be executed in the order they appear in the configuration file. Therefore, it is possible to remove a rule with SecFilterRemove and then add it again with SecFilterImport. Below you can find two examples that arrive at the same rule configuration, but take different routes to get there.

Note

If a target rule ID refers to a rule that is part of a chain, the import and remove directives will affect the whole chain, and not only the rule the ID refers to.

Example 1: the rules from the parent context are not inherited, but a single rule is imported.

SecFilter XXX id:1001
SecFilter YYY id:1002
SecFilter ZZZ id:1003

<Location /subcontext/>
    SecFilterInheritance Off
    SecFilterImport 1003
</Location>

Example 2: the rules from the parent context are inherited, with two rules removed.

SecFilter XXX id:1001
SecFilter YYY id:1002
SecFilter ZZZ id:1003

<Location /subcontext/>
    SecFilterRemove 1001 1002
</Location>

Note

The Apache web server supports many different types of context (e.g. <Directory>, <Location>, <Files>, ...). The order in which contexts are merged is significant. You should try not to mix inheritance and different-type contexts. If you have to, make sure you test the configuration to make sure it works as intended, and read the Apache context merging documentation carefully: http://httpd.apache.org/docs-2.0/sections.html#mergin.

Filter inheritance In multiuser environments

When you are deploying ModSecurity in multi-user environments, and your users are allowed to use the rules in their .htaccess files, you may not wish to allow them to not inherit the rules from the parent context. There are two ways to achieve this.

Note

If you do not trust your users (e.g. running in a web hosting environment) then you should never allow them access to ModSecurity. The .htaccess facility is useful for limited administration control decentralisation, keeping ModSecurity configuration with the application code. But it is not meant to be used in situations when the users may want to subvert the configuration. If you are running a hostile environment you should turn off the .htaccess facility completely by custom-compiling ModSecurity with the -DDISABLE_HTACCESS_CONFIG switch.

First, you can mark certain rules mandatory using the mandatory action. Such rules will always be inherited in the child context.

The other way is to use the SecFilterInheritanceMandatory directive to simply make all rules in the context mandatory for all child contexts.

SecFilterInheritanceMandatory On

Note

Just like SecFilterInheritance is always enabled in a context, SecFilterInheritanceMandatory is always disabled in a context, no matter of the value used in the parent context.

You may be wondering what happens in a situation like this one:

SecFilter XXX id:1001
SecFilterInheritanceMandatory On
<Location /subcontext/>
    SecFilterInheritance Off
    SecFilter YYY id:1002
    SecFilter ZZZ id:1003,mandatory
</Location>

<Location /subcontext/another/>
    SecFilterRemove 1001 1002 1003
    SecFilter QQQ id:1004
</Location>

Since rule inheritance is mandatory in the main context, the /subcontext/ context will inherit rule 1001 in spite of an attempt not to (using SecFilterInheritance Off). This subcontext will first run rule 1001, followed by the rules 1002 and 1003.

The mandatory rule 1001 from the main context will also propagate to context /subcontext/another/, in spite of an attempt to remove it. This is also true for the rule 1003, which was made mandatory for inheritance using the mandatory action. The SecFilterRemove 1001 1002 1003 directive will, however, succeed in removing rule 1002 because inheritance was not mandatory in /subcontext/. This context will therefore first run rule 1001 and 1003, followed by the rule 1004.

Note

You should avoid importing and removing rules that makes use of the skip action. Unless you are very careful you may end up with a configuration that does something other than what you intended.

URL Encoding Validation

Special characters need to be encoded before they can be transmitted in the URL. Any character can be replaced using the three character combination %XY, where XY represents an hexadecimal character code (see http://www.rfc-editor.org/rfc/rfc1738.txt for more details). Hexadecimal numbers only allow letters A to F, but attackers sometimes use other letters in order to trick the decoding algorithm. ModSecurity checks all supplied encodings in order to verify they are valid.

You can turn URL encoding validation on with the following line:

SecFilterCheckURLEncoding On

Note

This directive does not check encoding in a POST payload when the multipart/form-data encoding (file upload) is used. It is not necessary to do so because URL encoding is not used for this encoding.

Unicode Encoding Validation

Like many other features Unicode encoding validation is disabled by default. You should turn it on if your application or the underlying operating system accept/understand Unicode.

Note

More information on Unicode and UTF-8 encoding can be found in RFC 2279 (http://www.ietf.org/rfc/rfc2279.txt).

SecFilterCheckUnicodeEncoding On

This feature will assume UTF-8 encoding and check for three types of errors:

  • Not enough bytes. UTF-8 supports two, three, four, five, and six byte encodings. ModSecurity will locate cases when a byte or more is missing.

  • Invalid encoding. The two most significant bits in most characters are supposed to be fixed to 0x80. Attackers can use this to subvert Unicode decoders.

  • Overlong characters. ASCII characters are mapped directly into the Unicode space and are thus represented with a single byte. However, most ASCII characters can also be encoded with two, three, four, five, and six characters thus tricking the decoder into thinking that the character is something else (and, presumably, avoiding the security check).

Byte range check

You can force requests to consist only of bytes from a certain byte range. This can be useful to avoid stack overflow attacks (since they usually contain "random" binary content). To only allow bytes from 32 to 126 (inclusive), use the following directive:

SecFilterForceByteRange 32 126

Default range values are 0 and 255, i.e. all byte values are allowed.

Note

This directive does not check byte range in a POST payload when multipart/form-data encoding (file upload) is used. Doing so would prevent binary files from being uploaded. However, after the parameters are extracted from such request they are checked for a valid range.

Allowing others to see ModSecurity

Prior to 1.9 ModSecurity supported the SecServerResponseToken directive. When used, this directive exposed the presence of the module (with the version) in the web server signature. This directive no longer works in 1.9. If used, it will emit a warning message to the error log.