mod_security setup and domestication on your Apache server

If you’ve ever worked with a Web Application Firewall (WAF), you probably know how challenging it can be to deploy one in a real-world production environment. This is especially true when running it in blocking mode. Once enabled, a WAF can unexpectedly interfere with legitimate requests, causing certain features of the underlying application to stop working because requests are blocked and users receive 4xx HTTP status codes.

ModSecurity is a WAF module which integrates with Apache HTTP Server and analyses incoming traffic using predefined security rules. It is commonly deployed with the OWASP Core Rule Set (CRS), which provides protection against common web application attacks, this is how I installed it on Fedora Linux Server for my Nextcloud

Instalation and basic config

 dnf install httpd mod_security mod_security_crs

Restart Apache to load the module :

systemctl restart httpd

Check if is loaded:

 httpd -M | grep security

Expected:

security2_module (shared)

The main ModSecurity configuration file is typically located at: /etc/httpd/conf.d/mod_security.conf Edit /etc/httpd/conf.d/mod_security.conf and set:

SecRuleEngine DetectionOnly

In this mode, ModSecurity evaluates incoming HTTP requests and logs potential security violations, but does not execute disruptive actions defined by WAF rules.

SecAuditEngine RelevantOnly
SecAuditLog /var/log/httpd/modsec_audit.log
SecAuditLogParts ABIJDEFHZ

And here is the important thing: DetectionOnly does not disable ModSecurity. Security rules are still evaluated, and suspicious requests are logged. This makes it possible to understand how the WAF behaves with real production traffic before enforcing blocking rules.

Monitoring:

Look at files like this:

or monitor all events i you have more files like me:

tail -f /var/log/httpd/*

This is really helpful grep which you can use to find the Rule Ids that were triggered:

grep -oE '\[id "[0-9]+"\]' /var/log/httpd/* | sort | uniq -c | sort -nr

For Fedora installations using OWASP CRS 4, exclusions can typically be placed in:

/etc/httpd/modsecurity.d/activated_rules/REQUEST-900-EXCLUSION-RULES-BEFORE-CRS.conf

For example, suppose the /api/editor/save endpoint accepts HTML content and triggers false positives for rule 941100. Instead of disabling the rule globally, exclude a specific parameter for that endpoint:

SecRule REQUEST_URI "@streq /api/editor/save" "id:10001,phase:1,pass,nolog,t:none,ctl:ruleRemoveTargetById=941100;ARGS:content"

It is also possible to disable an entire rule for a particular endpoint:

SecRule REQUEST_URI "@streq /api/legacy"  "id:10002,phase:1,pass,nolog,t:none,ctl:ruleRemoveById=942100"

Validate and reload Apache:

apachectl configtest
systemctl reload httpd

Best practices for exclusions:

Change the mode

Finally, you can switch to active blocking mode; it is simply as simple as changing:

SecRuleEngine DetectionOnly

to

SecRuleEngine On

modsecurity can work on a side server in proxy mode with modproxy on Apache.

A long time ago, I implemented this for the website szkla.com. The architecture diagram:

+—————+ +——————————+ +———————+ | | | Apache WAF Server | | | | Client | —> | ModSecurity | —> | Backend Server | | | | mod_proxy | | | +—————+ +—————————+ +———————–+ | | Blocked v HTTP 403