Getting Started with Apache ModSecurity on Debian and Ubuntu

Powered by Drupal
Submitted by Sam Hobbs on

ModSecurityLogo.png ModSecurity is a Web Application Firewall: a program that can be used to inspect information as it passes through your web server, intercepting malicious requests before they are processed by your web application. This tutorial will show you how to install ModSecurity on Apache, and configure it with some sensible rules provided by the Open Web Application Security Project's Core Rule Set (OWASP CRS), which will help to protect your server against SQL injection, denial of service attacks, malformed requests, cross site scripting attacks, and more. And yes, you can use this guide with your Raspberry Pi if you're running Raspbian, Ubuntu, or another Debian derivative on it.

The Open Web Application Security Project's Core Rule Set (OWASP CRS)

ModSecurity is configured using a set of rules that provide the logic used to decide which requests to block. Probably the most common set of rules is the Open Web Application Security Project's Core Rule Set (OWASP CRS), which is in the repos as modsecurity-crs. The CRS can run in two modes: traditional and anomaly scoring. In traditional mode, the first rule that matches will block the request; in anomaly scoring mode the rules increment counters that "enumerate badness", and if the rule exceeds a threshold then the request is blocked. In this tutorial, we will configure Apache to run the core rule set in anomaly scoring mode, since it allows for a more intelligent approach to blocking.

Installing ModSecurity and the CRS

I'm assuming at this point that you already have Apache2 installed and some kind of web application (Wordpress, ownCloud etc.) running. If you don't, running the following commands will install apache2 as a dependency anyway, but it's a good idea to get something up and running on the server before you install ModSecurity. ModSecurity is a module for Apache2, so the name of the package follows the apache module conventions: libapache2-mod-security2. You can install it like this:

sudo apt-get update
sudo apt-get install libapache2-mod-security2

On most systems, this will pull in the CRS too, which is packaged as modsecurity-crs, as it is marked as a suggested package for modsecurity. This command will make sure it is set to manually installed so it won't get auto-removed:

sudo apt-get install modsecurity-crs

You can download new versions of the CRS directly from the project's GitHub repo, but don't! Some newer versions of the CRS make use of new features and directives in ModSecurity, so trying to run the newest rules with an "old" (read: not bleeding edge) version of ModSecurity will not work. I found this out the hard way...

Configuration

ModSecurity creates a directory at /etc/modsecurity during installation. This is the directory you should use to store all of your ModSecurity rules and configuration. The module configuration file that comes with ModSecurity will read any files in this directory that end in .conf when apache starts up. This is what the module config file looks like:

<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
        IncludeOptional /etc/modsecurity/*.conf
</IfModule>

This file should be at /etc/apache2/mods-available/security2.conf; there is also a file called security2.load that loads the module itself. We need to make sure that these two files are read by apache2 when it starts up, by creating symlinks into the /etc/apache2/mods-enabled directory. You could create the symlinks manually with ln -s, but the easiest and most reliable way is to use the a2enmod command:

sudo a2enmod security2

ModSecurity also makes use of the headers module, which we can enable like this:

sudo a2enmod headers

Don't reload apache2 yet!

ModSecurity Configuration File

At the moment, you should have two files in /etc/modsecurity: unicode.mapping and modsecurity.conf-recommended. The latter is a template config file. First we need to copy it so that it has a name that will be read by apache:

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

You shouldn't have to change much in this configuration file, but I'd like to bring your attention to the SecRuleEngine parameter, which decides whether ModSecurity is On, Off, or in DetectionOnly mode where it will process requests and write the audit log but not actually block anything:

SecRuleEngine DetectionOnly

You can leave this at DetectionOnly for now: there will be tons of false positives to begin with, and until you have written a whitelist (more on that later), turning the engine on will likely just break your site! When you have written the whitelist, you can change the parameter to On.

Filesystem Configuration

As can be seen in the module configuration file (/etc/apache2/mods-available/security2.conf above), ModSecurity creates a directory for storing temporary files at /var/cache/modsecurity during installation and makes it writable by Apache. The default configuration file specifies that temporary files should be stored in /tmp/, but this data is cleared after a reboot, whereas the data in /var/cache really is persistent. This is important because some of the session hijacking rules in the CRS check to see if secure session ID cookies set by web apps and PHP are ones that ModSecurity has seen before, and block the request if they are "new"... so if you log in to a web app like ownCloud and then reboot the web server and visit again later, the request will be blocked because the old cookie is "new" to ModSecurity. To fix this, navigate to the "Filesystem configuration" section in /etc/modsecurity/modsecurity.conf and change both SecTmpDir and SecDataDir from /tmp/ to /var/cache/modsecurity, i.e.

SecDataDir /var/cache/modsecurity

Now the session hijacking rules should work as intended (if you choose to enable them!).

Loading the CRS Config Files

The Core Rule Set is installed into /usr/share/modsecurity-crs/, and is separated into several directories such as base_rules, experimental_rules, optional_rules and slr_rules. SLR stands for SpiderLabs Research, which is the research team of the company that currently maintains the CRS. We are going to create symbolic links in the /etc/modsecurity directory that point to the rule files we want to load. You can ignore the activated_rules directory - on other distros or if you compiled everything from source, you might unpack the CRS tarball directly into /etc/modsecurity and then use the /etc/apache2/mods-enabled/security2.conf to make Apache read files in /etc/modsecurity/activated_rules instead of everything in the /etc/modsecurity directory. It's entirely up to you which rule files you choose to load, but at a minimum you should be using the base_rules. I would also recommend loading all of the optional_rules and then choosing a few of the experimental rules you think might be useful for your site or app (for example, I chose to enable the brute force and DoS protection rule files). You probably don't want to enable the appsensor_detection_point_* files (if you're curious what these do, refer to the OWASP documentation). Bear in mind that the experimental rules are likely to require more work tweaking/whitelisting to remove false positives for your site. Now, you could create each symlink individually, but that would be extremely tedious. If you want to use all of the rule files in one or more of those directories, this is much faster: For the base_rules:

cd /usr/share/modsecurity-crs/base_rules/
for ruleFile in * ; do sudo ln -s /usr/share/modsecurity-crs/base_rules/$ruleFile /etc/modsecurity/$ruleFile ; done

For the optional_rules:

cd /usr/share/modsecurity-crs/optional_rules/
for ruleFile in * ; do sudo ln -s /usr/share/modsecurity-crs/optional_rules/$ruleFile /etc/modsecurity/$ruleFile ; done

You probably don't want to enable all of the experimental_rules, but if you did you could use this command:

cd /usr/share/modsecurity-crs/experimental_rules/
for ruleFile in * ; do sudo ln -s /usr/share/modsecurity-crs/experimental_rules/$ruleFile /etc/modsecurity/$ruleFile ; done

For the slr_rules, I would recommend yo.u only load rule files when they are relevant to the type of site you run, e.g. if you have a wordpress site, you could run these two commands to load the configuration and data files:

sudo ln -s /usr/share/modsecurity-crs/slr_rules/modsecurity_crs_46_slr_et_wordpress_attacks.conf /etc/modsecurity/modsecurity_crs_46_slr_et_wordpress_attacks.conf
sudo ln -s /usr/share/modsecurity-crs/slr_rules/modsecurity_46_slr_et_wordpress_attacks.data /etc/modsecurity/modsecurity_46_slr_et_wordpress_attacks.data

You should now have a bunch of symlinks in the /etc/modsecurity directory pointing to the rule files, but we still need to copy across the most important file: modsecurity_crs_10_setup.conf, which contains configuration for the CRS itself. We symlinked the rule files so that when the package manager updates modsecurity-crs, a new version of the rule files will be automatically loaded by apache, meaning we will always have the latest version of the CRS for our version of ModSecurity. We will never edit the files directly to modify the rules, because those changes would be lost when the package is updated, instead we will create new rules in our own files that will override the defaults (more on that later). Since we intend to change the configuration in the modsecurity_crs_10_setup.conf file, we must copy it instead of symlinking so that we can make permanent changes:

sudo cp /usr/share/modsecurity-crs/modsecurity_crs_10_setup.conf /etc/modsecurity/modsecurity_crs_10_setup.conf

Configuring the CRS

Turn On Anomaly Scoring

The configuration in modsecurity_crs_10_setup.conf determines the blocking mode that the core rule set will use - the traditional vs anomaly scoring modes I mentioned in the introduction. SecDefaultAction determines the action taken by ModSecurity when a rule's action list contains the block keyword. This is unfortunate choice of language in my opinion, because the word "block" implies that the request will be intercepted, but that isn't necessarily the case depending on what SecDefaultAction is set to. I think the language is probably a relic from the way the CRS worked in early versions (traditional mode), where individual rules determined whether to intercept requests, and this parameter was primarily used to decide which phase to intercept the request in, and how to log it (i.e. it always blocked, but the details could be configured differently). Anyway, here's what the keywords mean:

  • The pass keyword means that ModSecurity will continue to process rules even though this rule matched.
  • To stop processing the transaction and intercept the request, rules can use the keyword deny.
  • In rare cases such as the Denial of Service rules, the rule may specify the drop action, which is even more severe: it closes the TCP connection completely by sending a FIN packet.

The default configuration is:

SecDefaultAction "phase:1,deny,log"

This means that the default block action intercepts the request in the first phase, and writes messages to both the error log and audit log. Since we don't want ModSecurity to decide whether to intercept requests until until all of the rules have been evaluated, locate that line in the file, and change it to this:

SecDefaultAction "phase:2,pass,log"

We also need to turn anomaly_score_blocking on by locating rule 900004 in the modsecurity_crs_10_setup.conf file under the "Collaborative Detection Blocking" heading and uncommenting the first line (the one with SecAction):

SecAction \
  "id:'900004', \
  phase:1, \
  t:none, \
  setvar:tx.anomaly_score_blocking=on, \
  nolog, \
  pass"

So, how does this result in blocking? If you look carefully at any of the rule files (modsecurity_crs_20_protocol_violations.conf would be a good place to start), you will notice that the action section of each rule sets transaction variables like this:

setvar:tx.anomaly_score=+%{tx.notice_anomaly_score},\

And messages like this:

setvar:'tx.%{rule.id}-OWASP_CRS/PROTOCOL_VIOLATION/INVALID_REQ-%{matched_var_name}=%{matched_var}'

If the rule matches, the anomaly_score variable is being incremented based on the severity of the rule. In this case the anomaly score was incremented by two points, in accordance with the variables set at the start of the transaction by a rule in modsecurity_crs_10_setup.conf under the "Collaborative Detection Severity Levels" heading:

SecAction \
  "id:'900001', \
  phase:1, \
  t:none, \
  setvar:tx.critical_anomaly_score=5, \
  setvar:tx.error_anomaly_score=4, \
  setvar:tx.warning_anomaly_score=3, \
  setvar:tx.notice_anomaly_score=2, \
  nolog, \
  pass"

The rule files are processed in alphanumerical order, so near the end of the chain in modsecurity_crs_49_inbound_blocking.conf you will find this rule, which uses the anomaly_score transaction variable:

# Alert and Block based on Anomaly Scores
#
SecRule TX:ANOMALY_SCORE "@gt 0" \
    "chain,phase:2,id:'981176',t:none,deny,log,msg:'Inbound Anomaly Score Exceeded (Total Score: %{TX.ANOMALY_SCORE}, SQLi=%{TX.SQL_INJECTION_SCORE}, XSS=%{TX.XSS_SCORE}): Last Matched Message: %{tx.msg}',logdata:'Last Matched Data: %{matched_var}',setvar:tx.inbound_tx_msg=%{tx.msg},setvar:tx.inbound_anomaly_score=%{tx.anomaly_score}"
        SecRule TX:ANOMALY_SCORE "@ge %{tx.inbound_anomaly_score_level}" chain
                SecRule TX:ANOMALY_SCORE_BLOCKING "@streq on" chain
                        SecRule TX:/^\d+\-/ "(.*)"

Looks a little scary, but roughly translated this means:

If the anomaly_score is greater than or equal to the inbound_anomaly_score_level, and anomaly_score_blocking is turned on, and there is a transaction variable that looks something like 000001-FOO/BAR, write a log message containing the score and variables that were matched and intercept the request

There's a similar rule for outbound blocking, which runs in a later phase and can intercept requests based on the outbound data to prevent information leakage. With a couple of exceptions such as the Denial of Service protection rules, when the CRS is run in anomaly scoring mode these two rules handle all of the interceptions, and the rest simply modify transaction variables to help them make a decision.

Use Brute Force and DoS Protection

If you enabled the brute force and DoS rule files, you need to modify some SecAction statements in modsecurity_crs_10_setup.conf that are used for these rules. This SecAction sets some transaction variables that are used in denial of service rules. The rules work by dropping new connections for a period of time (dos_block_timeout) if the number of requests exceeds a limit (dos_counter_thrreshold) in a certain amount of time (dos_burst_time_slice). You can probably leave the variables as they are, just uncomment the SecAction:

SecAction \
  "id:'900015', \
  phase:1, \
  t:none, \
  setvar:'tx.dos_burst_time_slice=60', \
  setvar:'tx.dos_counter_threshold=100', \
  setvar:'tx.dos_block_timeout=600', \
  nolog, \
  pass"

The brute force protection rules work in a similar way. This time you should edit the paths in brute_force_protected_urls so that the login urls for your server are protected, as well as uncommenting SecAction:

SecAction \
  "id:'900014', \
  phase:1, \
  t:none, \
  setvar:'tx.brute_force_protected_urls=#/user/login# #/wp_login.php#', \
  setvar:'tx.brute_force_burst_time_slice=60', \
  setvar:'tx.brute_force_counter_threshold=10', \
  setvar:'tx.brute_force_block_timeout=300', \
  nolog, \
  pass"

GeoIP

Under the GeoIP Database section, there is a SecGetoLookupDb parameter that is used to provide modsecurity with a database it can use to guess the country of clients by their IP addresses. If you want to use rules that make use of the database, make sure you have the GeoIP database installed:

sudo apt-get update
sudo apt-get install geoip-database

Then uncomment the line and update the path like this:

SecGeoLookupDb /usr/share/GeoIP/GeoIP.dat

Finishing Up

Now we've turned on ModSecurity, configured it to use the CRS, and set some sensible options, all that is left is to reload apache to read the new configuration:

sudo service apache2 reload

If you didn't get any errors, congratulations! ModSecurity is running in DetectionOnly mode. Now comes the difficult part: whitelisting to remove false positives, before you turn ModSecurity on for real. Before we dive into whitelisting, let's make sure you know how to read the three types of log file that are written by ModSecurity...

Reading the Logs (Error, Audit and Debug)

There are three types of log file that are written by ModSecurity:

  • The error log is the same log file that is used by Apache to write error messages, normally stored at /var/log/apache2/error.log.
  • The audit log is modsecurity's own log file, normally stored at /var/log/apache2/modsec_audit.log, which can contain a complete record of all the data in a transaction, and how ModSecurity processed it.
  • The debug log produces a huge amount of information about how each rule is evaluated and how transaction variables are set and incremented. As a result, it can grow very large very quickly, so you should only turn it on when you have to. Even then, it's a good idea to turn it on selectively for the specific types of request you are interested in. I won't spend any time explaining the debug log, because you hopefully won't have to use it!

Error log

Typically, each rule that matches will write a message to the error log, although this behaviour is controlled by the log or nolog keywords specified in the rule's action list, or in the default action we configured earlier. log means a message will be written to the error log and the audit log, and auditlog means the message will only be written to the auditlog, so if you only want to log in the audit log you must specify nolog,auditlog. Here's an example of a typical error log message from rule 960911, which verifies that the request line sent by the client follows the format specified in the RFC.

[Sun Mar 13 15:54:12.675099 2016] [:error] [pid 22832] [client 68.224.235.71] ModSecurity: Warning. Match of "rx ^(?i:(?:[a-z]{3,10}\\\\s+(?:\\\\w{3,7}?://[\\\\w\\\\-\\\\./]*(?::\\\\d+)?)?/[^?#]*(?:\\\\?[^#\\\\s]*)?(?:#[\\\\S]*)?|connect (?:\\\\d{1,3}\\\\.){3}\\\\d{1,3}\\\\.?(?::\\\\d+)?|options \\\\*)\\\\s+[\\\\w\\\\./]+|get /[^?#]*(?:\\\\?[^#\\\\s]*)?(?:#[\\\\S]*)?)$" against "REQUEST_LINE" required. [file "/etc/modsecurity/modsecurity_crs_20_protocol_violations.conf"] [line "52"] [id "960911"] [rev "2"] [msg "Invalid HTTP Request Line"] [data "CONNECT login.skype.com:443 HTTP/1.1"] [severity "WARNING"] [ver "OWASP_CRS/2.2.8"] [maturity "9"] [accuracy "9"] [tag "OWASP_CRS/PROTOCOL_VIOLATION/INVALID_REQ"] [tag "CAPEC-272"] [hostname "login.skype.com"] [uri "/"] [unique_id "VuWNJH8AAQEAAFkwANMAAAAI"]

Let's break this down a bit:

  • The first part of the log entry is a timestamp in the Apache format, followed by the process ID of the Apache handler that handled the request, and the client IP address.
  • Next is the test that was used in the matching rule, and the data that matched it.
  • The third part gives some information about the rule: which file it was loaded from, its position in the file, the unique ID of the rule, and an optional revision of the rule.
  • Following this is some data chosen by the rule. The message is set with the msg parameter (msg:'Invalid HTTP Request Line'); the data part is set with the logdata parameter, and is used to include the relevant part of the request that triggered the rule (logdata:'%{request_line}').
  • A series of CRS specific information follows this: the severity, version of the CRS that the rule belongs to, the rule "maturity" (how heavily tested the rule is, where 10 is the highest), the "accuracy" of the rule (where 10 is highest and scores below 5 indicate that there have been some false positives reported), and finally some tags that can be used to identify the category of attack that the rule was written to catch
  • The hostname and URI sent to the server are the domain name of the site the client wanted the information from, and the unique resource identifier (e.g. the URL) of the resource it wanted.
  • Finally, the unique_id is a unique identifier for that specific request, which can be used to identify the same request in the audit and debug logs.

When a transaction matches more than one rule, more than one log entry may be written, and the rules that do the collaborative blocking based on anomaly scores also write their own log messages on top of that. The error log is often the best place to look to get an overview of why a transaction has been blocked, but it doesn't contain the whole request sent to the server, and the complete reply. For that, you must look in the audit log.

Audit log

Sometimes, you need more information than the snippets of data selected by the rules that get printed in the error log. The audit log contains a record of complete transactions in text format. These three lines in /etc/modsecurity/modsecurity.conf set out what gets written to the audit log:

SecAuditEngine RelevantOnly
SecAuditLogRelevantStatus "^(?:5|4(?!04))"
SecAuditLogParts ABIJDEFHZ

Lines 1 and 2 mean that only requests with response codes 5XX (server error) or a 4XX (client error), excluding 404 (not found) are written to the audit log. This keeps the log size down, although you will find it still grows pretty quickly! Line 3 controls which parts of the audit log are written by default for matching requests. The parts available are:

  • A - audit log header
  • B - request headers
  • C - request body
  • D - intended response header (not implemented yet)
  • E - intended response body
  • F - response headers
  • G - response body
  • H - audit log trailer
  • I - reduced multipart request body
  • J - multipart files information
  • K - matched rules
  • Z - audit log footer

Some rules add to the default list of parts logged where that information is particularly useful or relevant, for example many of the outbound rules add part E to the log by specifying ctl:auditLogParts=+E in the action list. Most of the sections are pretty self explanatory, but I find the most useful section to be part H, which contains one message for each rule that was matched. You can get more information about each section of the log in the ModSecurity Documentation. When investigating intercepted transactions, you will often find yourself trying to find specific requests in the audit log after identifying them in the access and error logs. You may find it useful to copy the unique_id (which is in both of the other logs), and then after opening the audit log with less, type / and then paste the unique id (ctrl+shift+v) and press enter to search for the request. Here's the audit log entry that matches the error log snippet I used earlier:

--09989b78-A--
[13/Mar/2016:15:54:12 +0000] VuWNJH8AAQEAAFkwANMAAAAI 68.224.235.71 61122 192.168.1.2 80
--09989b78-B--
CONNECT login.skype.com:443 HTTP/1.1
Host: login.skype.com
Proxy-Connection: Keep-Alive

--09989b78-F--
HTTP/1.1 403 Forbidden
Content-Length: 283
Content-Type: text/html; charset=iso-8859-1

--09989b78-E--
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>403 Forbidden</title>
</head><body>
<h1>Forbidden</h1>
<p>You don't have permission to access /
on this server.

 

<hr> <address>Apache/2.4.7 (Ubuntu) Server at login.skype.com Port 443</address> </body></html> --09989b78-H-- Message: Warning. Match of "rx ^(?i:(?:[a-z]{3,10}\\s+(?:\\w{3,7}?://[\\w\\-\\./]*(?::\\d+)?)?/[^?#]*(?:\\?[^#\\s]*)?(?:#[\\S]*)?|connect (?:\\d{1,3}\\.){3}\\d{1,3}\\.?(?::\\d+)?|options \\*)\\s+[\\w\\./]+|get /[^?#]*(?:\\?[^#\\s]*)?(?:#[\\S]*)?)$" against "REQUEST_LINE" required. [file "/etc/modsecurity/modsecurity_crs_20_protocol_violations.conf"] [line "52"] [id "960911"] [rev "2"] [msg "Invalid HTTP Request Line"] [data "CONNECT login.skype.com:443 HTTP/1.1"] [severity "WARNING"] [ver "OWASP_CRS/2.2.8"] [maturity "9"] [accuracy "9"] [tag "OWASP_CRS/PROTOCOL_VIOLATION/INVALID_REQ"] [tag "CAPEC-272"] Message: Warning. Match of "within %{tx.allowed_methods}" against "REQUEST_METHOD" required. [file "/etc/modsecurity/modsecurity_crs_30_http_policy.conf"] [line "31"] [id "960032"] [rev "2"] [msg "Method is not allowed by policy"] [data "CONNECT"] [severity "CRITICAL"] [ver "OWASP_CRS/2.2.8"] [maturity "9"] [accuracy "9"] [tag "OWASP_CRS/POLICY/METHOD_NOT_ALLOWED"] [tag "WASCTC/WASC-15"] [tag "OWASP_TOP_10/A6"] [tag "OWASP_AppSensor/RE1"] [tag "PCI/12.1"] Apache-Error: [file "mod_authz_core.c"] [line 866] [level 3] AH01630: client denied by server configuration: %s%s Stopwatch: 1457884452674435 222736 (- - -) Stopwatch2: 1457884452674435 222736; combined=222248, p1=221271, p2=0, p3=75, p4=416, p5=395, sr=102, sw=91, l=0, gc=0 Response-Body-Transformed: Dechunked Producer: ModSecurity for Apache/2.7.7 (http://www.modsecurity.org/); OWASP_CRS/2.2.8. Server: Apache/2.4.7 (Ubuntu) Engine-Mode: "ENABLED" --09989b78-Z-- In this case, as you can see in the messages it was the base Apache configuration that denied the request, not ModSecurity (i.e. the request wasn't intercepted). However, two rules did match (960911 and 960032), and we can see some information about those. Here's what a request that is intercepted looks like:

--674ace60-A--
[13/Mar/2016:23:43:25 +0000] VuX7HX8AAQEAAGRzdAsAAAAG 192.168.1.1 41386 192.168.1.2 443
--674ace60-B--
GET /?test=test-attack HTTP/1.1
Host: samhobbs.co.uk
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:44.0) Gecko/20100101 Firefox/44.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
DNT: 1
Referer: https://samhobbs.co.uk/2015/09/example-whitelisting-rules-apache-modsecurity-and-owasp-core-rule-set
Cookie: Drupal.toolbar.collapsed=0; SSESSfoobar; has_js=1
Connection: keep-alive

--674ace60-F--
HTTP/1.1 403 Forbidden
X-Content-Type-Options: nosniff
X-Powered-By: PHP/5.5.9-1ubuntu4.14
Strict-Transport-Security: max-age=15768000
X-Clacks-Overhead: GNU Terry Pratchett
Content-Length: 707
Connection: close
Content-Type: text/html; charset=UTF-8

--674ace60-H--
Message: Access denied with code 501 (phase 1). Pattern match "test-attack" at ARGS:test. [file "/etc/modsecurity/modsecurity_crs_15_customrules.conf"] [line "22"] [id "000001"]
Action: Intercepted (phase 1)
Apache-Handler: application/x-httpd-php
Stopwatch: 1457912605362266 957 (- - -)
Stopwatch2: 1457912605362266 957; combined=300, p1=142, p2=0, p3=0, p4=0, p5=129, sr=43, sw=29, l=0, gc=0
Producer: ModSecurity for Apache/2.7.7 (http://www.modsecurity.org/); OWASP_CRS/2.2.8.
Server: Apache/2.4.7 (Ubuntu)
Engine-Mode: "ENABLED"

--674ace60-Z--

When I started whitelisting, I found that I wanted an easy way to sort through audit logs and inspect groups of requests (e.g. all of the requests from a certain IP address, or all requests that tripped a certain rule). This isn't something that is easily achieved using a text file where the information is split across multiple lines, so I wrote a commandline utility that can read the audit log into a sqlite database. At the time of writing it is in dire need of a rewrite because it was the first thing I wrote in C++, but it does work and it may be useful to you, so feel free to give it a go.

Whitelisting

Now that you have ModSecurity up and running, and you know how to inspect requests, it's time to write a whitelist. Whitelisting is not easy, but I hope my extensive guide with example whitelisting rules for ModSecurity will prove useful to you. You should also refer to the ModSecurity Reference Manual on GitHub, which is full of useful information. Once you have written your whitelisting rules, remember to set the SecRuleEngine On and reload apache! If you run more than one site or web app on your server, you might want to run ModSecurity in two different modes on different parts (e.g. DetectionOnly on one part while you whitelist, On on another part that you have finished whitelisting). You can do this, as well as setting custom log locations, by changing the settings in your virtualhost file. Make sure you use an IfModule guard so that the config is only loaded if ModSecurity is installed/running:

<IfModule mod_security2.c>
        SecRuleEngine On
        SecAuditLog     ${APACHE_LOG_DIR}/samhobbs/modsec_audit.log
        SecDebugLog ${APACHE_LOG_DIR}/samhobbs/modsec_debug.log
</IfModule>

If you have a bit of cash to spare, I would highly recommend buying the ModSecurity Handbook, which you can buy quite cheaply directly from the independent publisher. Again, you may find my commandline utility that can read the audit log into a sqlite database useful. If you want to learn more about the types of attack that the CRS protects against, I would recommend hacksplaining.com for a nice easy overview, followed by the OWASP website for articles with more technical detail.

Comments

I know, that's why I linked you the other comment thread where I linked to that commit...! I don't know if that solved all of your problems, does apache start now? Do you still have configtest errors? Sam

Hi Sam,

Apologies, I didn't see that thread until now for some reason. I left the website open while I was waiting and probably didn't refresh it.

Yes thanks, apache starts now and I have no config errors and I can see movement in the error and audit logs. I'll tackle the Whitelisting on the weekend.

Regards,

Jo

I found an issue with:

sudo ln -s /usr/share/modsecurity-crs/slr_rules/modsecurity_46_slr_et_wordpress_attacks.data /etc/modsecurity/modsecurity_46_slr_et_wordpress_attacks.data

I got this apache configtest error:

george@altopl1: /etc/modsecurity
==> sudo apachectl configtest
AH00526: Syntax error on line 17 of /etc/modsecurity/modsecurity_crs_46_slr_et_wordpress_attacks.conf:
Error creating rule: Could not open phrase file "/etc/modsecurity/modsecurity_46_slr_et_wordpress.data": No such file or directory
Action 'configtest' failed.

On Ubuntu 16.04, the Wordpress data file is:
george@altopl1: /etc/modsecurity
==> l /usr/share/modsecurity-crs/slr_rules/*wordpress*
/usr/share/modsecurity-crs/slr_rules/modsecurity_46_slr_et_wordpress.data

So I removed the incorrect linked file and did:

george@altopl1: /etc/modsecurity
==> sudo ln -s /usr/share/modsecurity-crs/slr_rules/modsecurity_46_slr_et_wordpress.data /etc/modsecurity/modsecurity_46_slr_et_wordpress.data

george@altopl1: /home/george
==> sudo apachectl configtest
Syntax OK

Add new comment

The content of this field is kept private and will not be shown publicly.

Filtered HTML

  • Web page addresses and email addresses turn into links automatically.
  • Allowed HTML tags: <a href hreflang> <em> <strong> <cite> <blockquote cite> <code> <ul type> <ol start type> <li> <dl> <dt> <dd>
  • Lines and paragraphs break automatically.