ModSecurity with OWASP CRS – Part 1: Installation

ModSecurity is an open source, cross platform Web Application Firewall (WAF) developed by Trustwave’s SpiderLabs. It has a robust event-based programming language which provides protection from a range of attacks against web applications and allows for HTTP traffic monitoring, logging and real-time analysis. ModSecurity is the most widely deployed WAF in existence.

Even though ModSecurity is the most used WAF, there are very few reliable guides(e.g netnea) on the internet on how to configure it properly. When I first installed ModSec, I used to copy exclusion rules(for false positives) from different guides on the internet, which I later learned is an extremely dangerous thing to do. Not only did some guides not explain what removing a rule would do, but also they’d just give you code to remove important WAF rules that’d defeat the entire purpose of installing ModSec in the first place!

Now, I’m not saying I’m an expert about ModSecurity and OWASP Core Rule Set, but what I’m going to do is explain the best practices for installing and using ModSec with OWASP Core Rule Set. Using ModSec with OWASP Core Rule Set is not an easy subject to explain, in one single post. Which is why, I’ll be writing multiple posts(hopefully only two) to explain all the necessary details to get up and running with ModSecurity. Although explaining how to write rule exclusions is not easy, I’ll try to make it as easy to understand and follow as possible. So without further ado, let’s get started!

Note:- What I’m going to write is based on my own experience and on OWASP Core Rule Set documentation. Also, for sake of brevity I’ll be referencing “OWASP Core Rule Set” as just “CRS”.

Table of Contents

Apache vs. Nginx

Installing ModSecurity WAF Engine

Downloading OWASP Core Rule Set

Setting up the configuration

Enabling Plugins as necessary

It’s ModSec game time!

Conclusion

Apache vs. Nginx

I’ll be using Apache webserver as a reference for this guide, but most of the guide can also be applied to Nginx webserver. Apache works well with ModSec v2.9.x and this is the one we’re going to be using in this guide, while ModSec v3 can be used with Nginx. As ModSecurity was initially developed as an Apache module, Apache still enjoys better ModSec support than Nginx. According to CRS Docs,

ModSecurity v3 fails with 2-4% of the CRS unit tests due to bugs and implementation gaps. Nginx + ModSecurity v3 also suffers from performance problems when compared to the Apache + ModSecurity v2 platform. This may be surprising for people familiar with the high performance of Nginx.

Source

Also, CRS recommends using ModSec v2.9.x with Apache over v3,

ModSecurity v3 is used in production together with Nginx, but the CRS project recommends to use the ModSecurity v2 release line with Apache.

Source

Installing ModSecurity WAF Engine

Now, before following further I expect you to have an Apache webserver running on an latest Debian based Linux distribution. Also, all the below commands can be just copy/pasted to achieve the desired results, but I want the user to read and understand what’s happening first. Now, If everything is ready, then you can install ModSecurity WAF engine with the following command:-

apt install libapache2-mod-security2

If you want to install the latest version of ModSecurity directly from source, then you can compile it with the help of different guides available. After installation, a new directory /etc/modsecurity will be created. Now, we need to copy the recommended ModSecurity configuration and enable the WAF rule engine. To create the recommended configuration use the following command:-

cp /etc/modsecurity/modsecurity.conf-recommended /etc/modsecurity/modsecurity.conf

Then we need to set the ModSec rule engine to On from DetectionOnly. To do this use the following command:-

sed -i 's|SecRuleEngine DetectionOnly|SecRuleEngine On|g' /etc/modsecurity/modsecurity.conf

Now, don’t restart Apache yet, as we need to do some extra configuration.

Downloading OWASP Core Rule Set

Now, this is a bit tricky part. According to CRS Docs,

For production environments, it is recommended to use the latest release, which is v3.3.4. For testing the bleeding edge CRS version, nightly releases are also provided.

Source

Now, you can download the latest release, which is v3.3.4, but the code of latest bleeding edge version, which is v4.0.0-rc1, will contain more bug/security fixes and a lot more added rules. I know this goes against the recommended solution and some would argue that it’s better to stick to the latest release(which is months old). If so, I want everyone against my argument to look at the image below and decide what’s the better option when it comes to security.

Default branch is 1886 commits ahead

As you can see above the latest default branch, which is v4.0/dev, is way ahead and obviously contains more rules, bugs and security fixes. Now that I’ve won the argument, let’s proceed with the installation. To download the latest code, first switch to the /etc/modsecurity directory:-

cd /etc/modsecurity

Then, clone the OWASP coreruleset git repo to get the latest code:-

git clone https://github.com/coreruleset/coreruleset.git

Setting up the configuration

Next, switch to the coreruleset directory and create the necessary configuration files(which we’ll edit later) from the .example files.

cd coreruleset
cp crs-setup.conf.example crs-setup.conf
cp rules/REQUEST-900-EXCLUSION-RULES-BEFORE-CRS.conf.example rules/REQUEST-900-EXCLUSION-RULES-BEFORE-CRS.conf
cp rules/RESPONSE-999-EXCLUSION-RULES-AFTER-CRS.conf.example rules/RESPONSE-999-EXCLUSION-RULES-AFTER-CRS.conf

Note:- The files REQUEST-900-EXCLUSION-RULES-BEFORE-CRS.conf.example and RESPONSE-999-EXCLUSION-RULES-AFTER-CRS.conf.example can be used to house “BEFORE-CRS” and “AFTER-CRS” rule exclusions in their correct places relative to the CRS rules. You’ll learn more about these in the second part of this series.

Next, we need to add these configuration files to ModSecurity WAF engine configuration, so that ModSec will use latest OWASP CRS code. To achieve this edit /etc/apache2/mods-available/security2.conf and change the configuration to the following:-

<IfModule security2_module>
        # Default Debian dir for modsecurity's persistent data
        SecDataDir /var/cache/modsecurity

        # Include all the *.conf files in /etc/modsecurity.
        # Keeping your local configuration in that directory
        # will allow for an easy upgrade of THIS file and
        # make your life easier
        Include /etc/modsecurity/modsecurity.conf
        Include /etc/modsecurity/coreruleset/crs-setup.conf

        Include /etc/modsecurity/coreruleset/plugins/*-config.conf
        Include /etc/modsecurity/coreruleset/plugins/*-before.conf

        Include /etc/modsecurity/coreruleset/rules/*.conf

        Include /etc/modsecurity/coreruleset/plugins/*-after.conf

</IfModule>

A lot of Includes need to be explained here. Basically, first we load the ModSec WAF engine configuration, then we enable the OWASP CRS configuration. Then we enable necessary plugin config and before files(you can learn about these here), then we enable all the CRS rules and then finally we load the plugin after file. Everything will be clear once we get to the second part of this series, for now just edit the file to reflect the above and save it.

Note:- The plugins directory contains three different empty files to act as a placeholder for the above configuration in case you decided to not use any plugins.

Enabling Plugins as necessary

OWASP Core Rule Set provides a lot of official plugins, which can be used to aid/enhance functionality of the default CRS rules. Note, in CRS 3.x the so-called plugins were included alongwith the CRS rules, but in CRS 4.0 they are separated and needed to be installed separately, as necessary. There are multiple types of plugins available which serve different functionality, but for demo purposes I’ll be installing NextCloud and WordPress rule exclusion plugins which help in reducing false positives in NextCloud and WordPress respectively.

To download the plugins, use the following commands:-

cd /etc/modsecurity
git clone https://github.com/coreruleset/nextcloud-rule-exclusions-plugin.git nextcloud
git clone https://github.com/coreruleset/wordpress-rule-exclusions-plugin.git wordpress

Now, we need to add these plugin rules to our ModSec rules. You can see that earlier we’ve Included the *-config, *-before and *-after config files in /etc/apache2/mods-available/security2.conf above, so now we’ve two options. First, we can just copy the plugin config files to our CRS plugins folder and these will be automatically loaded. Second, we can create symlinks to these files in CRS plugins folder, this way you can just switch to the NextCloud or WordPress plugin directory and do a git pull and you’ll have the latest plugin code running once you’ve restarted Apache. I follow the second method, so here are the commands to do it:-

cd /etc/modsecurity/coreruleset/plugins
ln -sf /etc/modsecurity/nextcloud/plugins/nextcloud-rule-exclusions-before.conf nextcloud-before.conf
ln -sf /etc/modsecurity/wordpress/plugins/wordpress-rule-exclusions-before.conf wordpress-before.conf

Your CRS plugins folder should look something like this:-

It’s ModSec game time!

Now that everything is setup including our necessary plugins, getting started with ModSec is as simple as restarting Apache, but there can be a small problem depending upon your ModSec WAF engine version. The Stable/LTS version of Debian/Ubuntu ships with ModSec version 2.9.5. If you’re using this version, then Apache will fail to start because of this error:-

If you look into the file /etc/modsecurity/coreruleset/rules/REQUEST-922-MULTIPART-ATTACK.conf causing the error you’ll see the following:-

As you can see, this file was added recently addressing a new vulnerability and will likely be missing for CRS 3.x rules release line. This only bolsters my earlier argument that using the latest code is always the safest! But, if you’ve ModSec version 2.9.5 or earlier the only thing you can do is temporarily move this file and wait until your Linux distribution provides an update(and hope this vulnerability doesn’t appear on your WebApp (;) or you can compile ModSec from scratch! What you do next is entirely your decision. But, I accepted the risk and moved the file so that Apache will restart successfully. If you want to do so, then use the following commands:-

mv /etc/modsecurity/coreruleset/rules/REQUEST-922-MULTIPART-ATTACK.conf /etc/modsecurity/coreruleset/rules/REQUEST-922-MULTIPART-ATTACK.conf.bak
systemctl restart apache2

Note:- If you think you’ve learnt about a vulnerability my WAF will not protect against, then try hacking it. I don’t run state-of-the-art security in my server for nothing. (-;

Update:- I compiled ModSecurity v2.9.6 and installed it. Now, all rules are working. It’s super easy to compile. Also, after the day I wrote this blog post my error.log touched 30MB+ in a single day! I never thought there will be such overwhelming response. Lesson learnt, “Never brag publicly!”

And that’s it! You’ve successfully enabled ModSecurity with the latest and greatest OWASP CRS WAF rules. And if you’re using NextCloud or WordPress, then you’ve also added rule exclusion plugins for them, which’ll give you peace of mind if you decide to stick with OWASP CRS Paranoia level 1! What’s a Paranoia level? Well, that’s another story. (-;

Also, before ending this I want to include one more thing for people who like little hackity hacks, like me. I didn’t mention earlier but one great benefit about downloading OWASP CRS by cloning the source repo is that upgrading WAF rules has become very easy. You just need to switch to /etc/modsecurity/coreruleset directory and do a git pull and “BAM!” you’ve the latest code with you. To make this easier, you can add a little hack like the following as a cron job to automatically update the CRS code.

#!/bin/bash

# Switch to coreruleset directory
cd /etc/modsecurity/coreruleset

# Pull the latest code
git pull

# CRS checks the tx.crs_setup_version variable to ensure that the setup has been loaded. We need the latest version.
VERSION=$(head -n 2 crs-setup.conf.example | awk -F. '{ printf $2$3$4 }' | grep -oP "\d{3}")

# Change crs-setup.conf to reflect the latest version
sed -i "s/crs_setup_version=.*\?\"/crs_setup_version=$VERSION\"/g" crs-setup.conf

# Uncomment the following line if you're sure you can fix problems in the rare case Apache fails to start.
#systemctl restart apache2

Now, uncomment the last line if you’re sure you can fix Apache easily. I normally get emails for cron jobs, so I’ll know the instant this script runs and will be able to fix the error fast without any significant downtime. But as I said earlier, this is a hackity hack and although the chances of Apache failing is very low, you use this at your own risk!

Note:- For Enterprises, Administrators must test the latest OWASP CRS rules in a development environment, if available. Once its tested for false positives, the rules can be deployed into Production.

Conclusion

So, there goes the easy part about ModSecurity. Hope, you’ll enjoy your to be super secure WebApp. Now, you’re safe to run on Paranoia Level 1, but if you want to increase it then you should do so only after reading the next part and feeling confident that you can handle false positives easy peasy. The next part of this series will be all technical and you’ll learn how to write WAF exclusion rules from scratch! Don’t worry if you’re not good at programming, you can write rule exclusion rules if you’ve basic technical understanding of how the web works. The fun part is, I’ll be explaining the same gut-wrenching stuff that used to make me go crazy when I looked at them a year ago. Well, it’s only “not easy” until you learn how.

Thanks for reading. Peace!

Leave a Reply

Your email address will not be published. Required fields are marked *