Securing Drupal with ModSecurity and the Core Rule Set (CRS3)

The new Core Rule Set 3.0 (CRS3) release simplifies ModSecurity/Drupal integration tremendously. Here is a guide aimed at the Drupal community to learn how to work with ModSecurity. This guide and the rule file it is based on currently covers Drupal Core. Modules / Plugins are not yet supported. But count on the Drupal community to help us close this wide gap.

OWASP ModSecurity Core Rule Set is a horrible name for a project, that’s why we speak of CRS3. This is a security project and for those not familiar with the CRS, I will give a brief intro first.


CRS3 Release Poster

The poster of the CRS3 release.

ModSecurity is an open source Web Application Firewall (WAF) meant to protect applications exposed on the internet from all sort of nastiness; namely those attacks summarized as the OWASP Top Ten. So ModSecurity is meant to protect you and your Drupal installation from SQL injections, Cross Site Scripting attacks, you name it. Drupal is a fine piece of software and the security record is quite good given it is an advanced content management system. But an in-depth approach to security will plan ahead and a ModSecurity installation will help defending against new attacks before the Drupal Security team had a chance to react.

The Core Rule Set

ModSecurity is the engine, but it is quite naked without the rule set. And this is where the OWASP ModSecurity Core Rule Set comes in. That is a set of 150 – 200 generic blacklisting rules to examine http traffic and decide if it smells like an attack or not. This is performed via a set of regular expressions. Some of them fairly complex and some of them giving false alarms when they see legitimate Drupal traffic. That is natural for a heuristic approach because Drupal has a few habits that make it look suspicious to the CRS3 and we need to deal with these situations. Good for us, CRS3 comes with a set of predefined rule exclusions to deal with that problem. That means that there is an optional configuration file that instructs ModSecurity to ignore certain rules for certain Drupal parameters. With 150 rules active, removing a few is usually not a big deal and certainly better than not running ModSecurity at all.

We call CRS3 the 1st line of defense. It is meant to keep most of the bad stuff away. However, we have no illusions that evasion is always possible. But CRS3 makes it harder, a lot harder, for an attacker to get to your Drupal installation.


I am assuming you have Drupal already installed or you know how to do that. If not, then I suggest you check out this link.

So let’s install ModSecurity. This depends on your webserver and your distro. ModSecurity works with Apache, NginX and IIS. If you are running Apache on a OS from the Debian/Ubuntu family, then simply install the package as follows:

$> sudo apt-get install libapache2-mod-security2

Then you need to install the CRS3. There is an INSTALL file which explains things or you can follow my in-depth tutorial. Here I give you an abbreviated version installing into the existing /etc/modsecurity folder on an Ubuntu system:

$> cd /etc/modsecurity
$> wget
$> tar xvzf v3.0.0.tar.gz
$> mv owasp-modsecurity-crs-3.0.0/ owasp-modsecurity-crs
$> cd owasp-modsecurity-crs
$> cp crs-setup.conf.example crs-setup.conf

Here we have downloaded the rules, untarred them, renamed the folder and copied an example configuration file in that folder. Now we need to include this configuration file and the rules. This is done via a pair of Apache include statements. The natural choice for such an include is to extend the existing configuration in /etc/apache2/mods-enabled/security2.conf (again Debian/Ubuntu family; likely somewhere else for different distributions). Add the following lines in the right location (The IfModule will be already there. Move to the end of that section):

<IfModule security2_module>


    # OWASP ModSecurity Core Rule Set Inclusion
    # CRS configuration

    Include /etc/modsecurity/owasp-modsecurity-crs/crs-setup.conf

    # CRS runtime rule exclusions
    # ...

    # CRS rules inclusion

    Include /etc/modsecurity/owasp-modsecurity-crs/rules/*.conf

    # CRS startup time rule exclusions
    # ...


If you happen to run a long-term support Linux OS you might end up with an old Apache version plagued by a nasty bug which was only fixed in 2.4.11. If that is the case, then please look up the workaround in the KNOWN_BUGS file.

What is important is the file /etc/modsecurity/owasp-modsecurity-crs/crs-setup.conf. This is where you tweak important configuration items. The defaults are very sane and there are only the optional Drupal rules that we need to activate in the file:

SecAction \

Then we need to put ModSecurity in blocking mode and set two default values that will bring a better CRS experience. Find the following directives in /etc/modsecurity/modsecurity.conf-recommended and set the values as follows:


SecRuleEngine On


SecPcreMatchLimit 500000
SecPcreMatchLimitRecursion 500000


CRS3 in Production

With this, CRS3 is now active. Let’s restart the webserver to have it pick up the new configuration and let’s try this out with a simple test to see if we get blocked (replace localhost with your URI):


Drupal Forbidden Screenshot

CRS protecting Drupal from Remote Command Execution

The predefined rule exclusions help you avoiding blockades where you expect Drupal or your users to enter funny keywords like the one above. This means, it is OK to write about /bin/bash in a Drupal Post and the CRS will not complain. But filling the said value as your username outside an article or post will lead to the desired block.

The optional rule exclusions only cover Drupal core for the time being. We are aware this is only a start and the Core Rule Set team understands that this is certainly not enough to support complex sites with many different modules. This is where you, member of the Drupal community, come in: You need to help us with the additional modules and the false alarms CRS3 triggers. Here are your options:

We will then try and cover the problem in one of the subsequent releases.

But how about immediate help?

If you are blocked due to a false positive, you can help yourself pass the said payload in a subsequent request by making ModSecurity ignore all requests from your IP address ( in the example) as follows:

SecRule REMOTE_ADDR "@ipMatch" "id:10000,phase:1,pass,ctl:ruleEngine=Off"

Note that there is a mandatory id being set in this declaration. This id has to be unique. If you deploy multiple declarations in this manner, you need to use different ids with each of them (Please also note that this has to be placed before the Include statement. We reserved a space in the rule file for this named CRS runtime rule exclusions).

A more diligent handling of the problem involves writing rule exclusions. There is an extensive tutorial with detailed coverage summary of the problem and a good exercise following a very clear rule exclusion policy.

Here is the short version: Look into the Apache error log and search for ModSecurity rule alerts:

[2016-10-25 08:40:01.884172] [-:error] WA7@QX8AAQEAABC4maIAAAAV [client] ModSecurity: Warning. Matched phrase "/bin/bash" at ARGS:exec. [file "/apache/conf/crs/rules/REQUEST-932-APPLICATION-ATTACK-RCE.conf"] [line "448"] [id "932160"] [rev "1"] [msg "Remote Command Execution: Unix Shell Code Found"] [data "Matched Data: /bin/bash found within ARGS:exec: /bin/bash"] [severity "CRITICAL"] [ver "OWASP_CRS/3.0.0"] [maturity "1"] [accuracy "8"] [tag "application-multi"] [tag "language-shell"] [tag "platform-unix"] [tag "attack-rce"] [tag "OWASP_CRS/WEB_ATTACK/COMMAND_INJECTION"] [tag "WASCTC/WASC-31"] [tag "OWASP_TOP_10/A1"] [tag "PCI/6.5.2"] [hostname "localhost"] [uri "/index.html"] [unique_id "WA7@QX8AAQEAABC4maIAAAAV"]

They contain the keyword ModSecurity which makes them easy to grep. In our example, we see the rule with ID 932160 complaining about ARGS:exec (short for “parameter named exec”).

This is a real attack, but the client’s IP tells you that it has been my own test. If you see such an alert in the wild and you are sure this was a legitimate request, then you can disable the inspection of this parameter by this rule as follows (place after the include statement as it is a startup time rule exclusion:

SecRuleUpdateTargetById 932160 "!ARGS:exec"

This is a very simple remedy, but it has very wide consequences: It is a site-wide rule exclusion. This means the parameter exec is now ignored by rule 932160 completely. For a permanent install, I advocate a more subtle approach limiting the exclusion to certain paths or forms like outlined in the tutorial. However, the statement above this will do as a temporary bandaid that lets you continue your work.

For a start with the ModSecurity CRS3, this is all you need to know. Give it a go!

Was this helpful? What else should go into this document? Any mistakes? I would like to hear from you! Get in touch via email or send me a message on twitter (@ChrFolini).

Otherwise, look around here on for more ModSecurity resources and check out the courses I am offering via FeistyDuck.

Christian Folini

[EDIT: Added note in the introduction, that CRS3 only covers Drupal Core so far (this was hidden in the text somewhere before).]