NGINX / Security / Server Setup

Using the OWASP Core Rule Set with the NGINX Security Module

by , , revisited on


We have by far the largest RPM repository with NGINX module packages and VMODs for Varnish. If you want to install NGINX, Varnish, and lots of useful performance/security software with smooth yum upgrades for production use, this is the repository for you.
Active subscription is required.

In this comprehensive guide, we explain how to install, configure, and test the Open Web Application Security Project Core Rule Set (OWASP CRS) with the NGINX ModSecurity module on Rocky Linux, AlmaLinux, and RHEL 9/10. The OWASP CRS is one of the most effective web application firewall (WAF) rule sets available, providing enterprise-grade security against the most common web attacks.

The NGINX Extras repository provides both the NGINX ModSecurity module (nginx-module-security) and ready-to-use OWASP CRS packages. Together, they protect your web applications against SQL Injection (SQLi), Remote Code Execution (RCE), Local File Inclusion (LFI), Cross-Site Scripting (XSS), and many other attacks listed in the OWASP Top 10.

What is the OWASP Core Rule Set?

The OWASP Core Rule Set (CRS) is a set of generic attack detection rules designed to work with ModSecurity or compatible web application firewalls. Unlike signature-based detection systems that require constant updates to catch new threats, CRS uses anomaly scoring and generic attack patterns to identify malicious requests—even zero-day exploits.

Key Features of OWASP CRS 4.x

The latest OWASP CRS version 4.x brings significant improvements over previous versions:

  • Anomaly Scoring Mode: Instead of blocking on the first matched rule, CRS accumulates a threat score across multiple rules. This dramatically reduces false positives while maintaining strong security.
  • Paranoia Levels: Four configurable paranoia levels (PL1 through PL4) allow you to balance security strictness against false positive rates. PL1 is suitable for most applications, while PL4 provides maximum protection for high-security environments.
  • OWASP Top 10 Coverage: Comprehensive protection against all categories in the OWASP Top 10, including injection attacks, broken authentication patterns, and security misconfigurations.
  • Reduced False Positives: CRS 4.x includes extensive testing and tuning to minimize false positives in production environments.
  • Plugin Architecture: Extend functionality with official and community plugins for specific applications like WordPress, Drupal, and Nextcloud.

Prerequisites

Before you begin, ensure you have:

Installing the Repository

First, install EPEL and the GetPageSpeed Extras repository:

sudo dnf -y install epel-release
sudo dnf -y install https://extras.getpagespeed.com/release-latest.rpm

Installing NGINX with ModSecurity and OWASP CRS

Next, install NGINX, the ModSecurity connector module, and the OWASP CRS package in a single command:

sudo dnf -y install nginx nginx-module-security nginx-owasp-crs

This command installs the following packages:

  • nginx: The high-performance NGINX web server
  • nginx-module-security: The ModSecurity v3 NGINX connector module
  • nginx-owasp-crs: The OWASP CRS rule set pre-configured for NGINX

Configuring NGINX with ModSecurity

Step 1: Load the ModSecurity Module

Edit your main NGINX configuration file /etc/nginx/nginx.conf and add the module load directive at the very top, before any other directives:

load_module modules/ngx_http_modsecurity_module.so;

Step 2: Enable ModSecurity in Your Server Block

The OWASP CRS package provides a ready-to-use configuration file at /etc/nginx/modsec_includes.conf. To enable ModSecurity for a site, add these directives to your server block:

server {
    listen 80;
    server_name example.com;

    modsecurity on;

    location / {
        modsecurity_rules_file /etc/nginx/modsec_includes.conf;
        root /var/www/html;
        index index.html index.php;
    }
}

Step 3: Test and Reload NGINX

Finally, verify your configuration is valid, then reload NGINX:

sudo nginx -t
sudo systemctl reload nginx

Understanding the OWASP CRS Configuration

The OWASP CRS configuration is organized into several key files:

Main Configuration Files

File Purpose
/etc/nginx/modsecurity.conf Core ModSecurity settings (rule engine, body access, limits)
/etc/nginx/modsec_includes.conf Master include file that loads all CRS components
/etc/nginx/owasp-modsecurity-crs/crs-setup.conf CRS-specific settings (paranoia level, anomaly thresholds)

Rule Categories

The CRS includes specialized rule files for different attack types:

Rule File Protection Against
REQUEST-913-SCANNER-DETECTION.conf Automated vulnerability scanners
REQUEST-920-PROTOCOL-ENFORCEMENT.conf HTTP protocol violations
REQUEST-921-PROTOCOL-ATTACK.conf HTTP smuggling, request splitting
REQUEST-930-APPLICATION-ATTACK-LFI.conf Local file inclusion attacks
REQUEST-931-APPLICATION-ATTACK-RFI.conf Remote file inclusion attacks
REQUEST-932-APPLICATION-ATTACK-RCE.conf Remote code execution (OS commands)
REQUEST-933-APPLICATION-ATTACK-PHP.conf PHP-specific injection attacks
REQUEST-941-APPLICATION-ATTACK-XSS.conf Cross-site scripting (XSS)
REQUEST-942-APPLICATION-ATTACK-SQLI.conf SQL injection attacks
REQUEST-943-APPLICATION-ATTACK-SESSION-FIXATION.conf Session fixation attacks
RESPONSE-950-DATA-LEAKAGES.conf Information disclosure in responses
RESPONSE-955-WEB-SHELLS.conf Web shell detection

Testing Your WAF Configuration

Creating a Test Application

To test the WAF without affecting production, create a simple test site. First, create the file /etc/nginx/conf.d/test-waf.conf:

server {
    listen 8099;
    server_name localhost;

    modsecurity on;

    location / {
        modsecurity_rules_file /etc/nginx/modsec_includes.conf;
        root /usr/share/nginx/html;
        index index.html;
    }
}

Then, create a simple HTML file and reload NGINX:

echo "<html><body>Hello World</body></html>" | sudo tee /usr/share/nginx/html/index.html
sudo nginx -s reload

Now you can verify the setup with a normal request:

curl http://localhost:8099/

You should see: <html><body>Hello World</body></html>

Testing Attack Detection

Let’s verify that the WAF blocks various attack types. First, try sending a SQL injection attack:

curl "http://localhost:8099/?id=1'%20UNION%20SELECT%20*%20FROM%20users--"

The CRS should return a 403 Forbidden response, indicating the attack was blocked.

Next, verify XSS detection works correctly:

curl "http://localhost:8099/?input=%3Cscript%3Ealert(1)%3C/script%3E"

Additionally, check that Local File Inclusion attempts are blocked:

curl "http://localhost:8099/?file=../../../etc/passwd"

Furthermore, confirm Remote Code Execution pattern detection:

curl "http://localhost:8099/?cmd=;cat%20/etc/passwd"

Finally, verify Scanner Detection using a Nikto user agent:

curl -H "User-Agent: Nikto" "http://localhost:8099/"

All of these requests should return 403 Forbidden, confirming the CRS is protecting your application.

Using Nikto for Comprehensive Testing

Nikto is a popular vulnerability scanner that generates thousands of malicious requests. Therefore, it’s excellent for testing your WAF configuration:

sudo dnf -y install nikto
nikto -h localhost:8099

Without the CRS enabled, Nikto typically reports hundreds of potential vulnerabilities. However, with CRS enabled, most of these requests are blocked, drastically reducing the reported items.

Understanding ModSecurity Logs

When ModSecurity blocks a request, it logs detailed information to the NGINX error log. A typical blocked request entry looks like this:

ModSecurity: Access denied with code 403 (phase 2). Matched "Operator `Ge' with parameter `5' against variable `TX:BLOCKING_INBOUND_ANOMALY_SCORE' (Value: `20' ) [file "/etc/nginx/owasp-modsecurity-crs/rules/REQUEST-949-BLOCKING-EVALUATION.conf"] [line "222"] [id "949110"] [msg "Inbound Anomaly Score Exceeded (Total Score: 20)"] [ver "OWASP_CRS/4.22.0"] [tag "anomaly-evaluation"] [tag "OWASP_CRS"]

The log contains several key pieces of information:

  • Total Score: 20: The accumulated anomaly score (threshold is 5)
  • id “949110”: The blocking evaluation rule that enforced the threshold
  • ver “OWASP_CRS/4.22.0”: OWASP CRS version

Tuning OWASP CRS for Production

Adjusting the Paranoia Level

The paranoia level controls how aggressively the CRS detects threats. To change it, uncomment and edit the SecAction in /etc/nginx/owasp-modsecurity-crs/crs-setup.conf:

SecAction \
  "id:900000,\
   phase:1,\
   pass,\
   t:none,\
   nolog,\
   tag:'OWASP_CRS',\
   ver:'OWASP_CRS/4.22.0',\
   setvar:tx.blocking_paranoia_level=1"

Paranoia Level Guidelines:

Level Use Case False Positive Rate
PL1 Standard websites, general protection Low
PL2 E-commerce, user-facing applications Moderate
PL3 Banking, healthcare applications Higher
PL4 Maximum security environments Highest

For most websites, PL1 (default) provides excellent protection with minimal false positives.

Configuring Anomaly Score Thresholds

CRS uses anomaly scoring to determine when to block requests. By default, the inbound threshold is 5, meaning a request must accumulate 5 or more anomaly points to be blocked. You can adjust this in crs-setup.conf:

SecAction \
  "id:900110,\
   phase:1,\
   pass,\
   t:none,\
   nolog,\
   setvar:tx.inbound_anomaly_score_threshold=5,\
   setvar:tx.outbound_anomaly_score_threshold=4"

Keep in mind that:

  • Lower thresholds = stricter blocking (more false positives)
  • Higher thresholds = more lenient blocking (potentially less security)

Creating Rule Exclusions

If legitimate requests are being blocked, you should create exclusions rather than disabling rules entirely. Add custom exclusions to /etc/nginx/owasp-modsecurity-crs/rules/REQUEST-900-EXCLUSION-RULES-BEFORE-CRS.conf:

# Example: Exclude a specific parameter from SQL injection checks
SecRule REQUEST_URI "@beginsWith /api/search" \
  "id:1000,\
   phase:1,\
   pass,\
   t:none,\
   nolog,\
   ctl:ruleRemoveTargetById=942100;ARGS:query"

Advanced Configuration Topics

Enabling IP Reputation with Project Honeypot

CRS supports IP reputation checking via Project Honeypot. To enable this feature, add your API key to crs-setup.conf:

SecHttpBlKey your_api_key_here
SecAction "id:900500,phase:1,pass,t:none,nolog,setvar:tx.block_suspicious_ip=1"

GeoIP-Based Blocking

With the libmaxminddb library (automatically installed), you can enable country-based blocking:

SecGeoLookupDB /usr/share/GeoIP/GeoLite2-Country.mmdb
SecRule REMOTE_ADDR "@geoLookup" "chain,id:900600,phase:1,deny,log"
SecRule GEO:COUNTRY_CODE "@pm CN RU" "t:none"

Rate Limiting with ModSecurity

You can also protect against brute force and DoS attacks using rate limiting:

SecAction "id:900700,phase:1,pass,t:none,nolog,setvar:ip.request_count=+1,expirevar:ip.request_count=60"
SecRule IP:REQUEST_COUNT "@gt 100" "id:900701,phase:1,deny,status:429,log,msg:'Rate limit exceeded'"

Keeping OWASP CRS Updated

The OWASP CRS is actively maintained with regular updates that include new attack signatures and reduced false positives. Therefore, you should update your installation regularly:

sudo dnf update nginx-owasp-crs
sudo systemctl reload nginx

The GetPageSpeed repository ensures you receive the latest stable CRS releases with proper packaging for RHEL-based systems.

Troubleshooting Common Issues

False Positives on Legitimate Requests

If you encounter false positives, follow these steps:

  1. Check the error log for the rule ID that triggered
  2. Evaluate if the rule is appropriate for your application
  3. Create a targeted exclusion rather than disabling the rule

Performance Considerations

ModSecurity with CRS adds some processing overhead. To optimize performance, consider:

  • Setting appropriate SecRequestBodyLimit values
  • Disabling response body inspection if not needed: SecResponseBodyAccess Off
  • Using SecRuleRemoveById to disable unnecessary rules

ModSecurity Not Blocking Attacks

If attacks are not being blocked, verify the following:

  1. SecRuleEngine On is set in /etc/nginx/modsecurity.conf
  2. The modsecurity on; directive is in your server block
  3. The modsecurity_rules_file path is correct
  4. NGINX was reloaded after configuration changes
  5. You’re serving actual content (not using return directives which bypass phase 2 checks)

Conclusion

The OWASP Core Rule Set combined with ModSecurity provides robust protection for NGINX-powered web applications against a wide range of attacks. By following this guide, you’ve implemented enterprise-grade WAF security using packages from the GetPageSpeed Extras repository.

In summary, here are the key takeaways:

  • OWASP CRS 4.x offers superior attack detection with reduced false positives through anomaly scoring
  • Pre-packaged installation via nginx-owasp-crs simplifies deployment on RHEL-based systems
  • Paranoia levels allow you to balance security strictness with usability
  • Regular updates ensure protection against emerging threats

For more information about installing the ModSecurity module, see How to install the ModSecurity NGINX module in Rocky Linux/AlmaLinux/RHEL 9.


Frequently Asked Questions

Q: What’s the difference between ModSecurity and OWASP CRS?

ModSecurity is the WAF engine that processes requests, while OWASP CRS is the rule set that tells ModSecurity what attacks to detect. You need both for effective protection.

Q: Will OWASP CRS slow down my website?

With proper configuration, the impact is minimal (typically 1-5ms per request). As a result, the security benefits far outweigh the minor performance cost.

Q: How do I know if attacks are being blocked?

Monitor /var/log/nginx/error.log for ModSecurity denial messages. Additionally, consider setting up alerting for high-severity blocks.

Q: Can I use OWASP CRS with NGINX Plus?

Yes, the same module and rules work with both open-source NGINX and NGINX Plus.

Q: What attacks does OWASP CRS block?

CRS blocks SQL injection, XSS, local/remote file inclusion, remote code execution, HTTP protocol attacks, scanner detection, session fixation, and many more attack categories covered in the OWASP Top 10.

Q: How does anomaly scoring work?

Each rule that matches a suspicious pattern adds points to an anomaly score. At the end of request processing, if the total score exceeds the threshold (default: 5), the request is blocked. Consequently, this approach allows multiple low-confidence indicators to combine into high-confidence attack detection.

Q: What is the recommended paranoia level for WordPress sites?

For most WordPress sites, Paranoia Level 1 (default) provides excellent protection. If you experience false positives with certain plugins, create targeted exclusions rather than lowering security. Furthermore, WordPress-specific plugins for CRS are available to reduce common false positives.

Q: How can I test if ModSecurity is working without blocking legitimate traffic?

Set SecRuleEngine DetectionOnly in /etc/nginx/modsecurity.conf. This logs potential attacks without blocking them, allowing you to identify false positives before enabling enforcement mode.

Security Best Practices

When deploying OWASP CRS in production, follow these security best practices:

Start in Detection Mode

Before enabling blocking mode, run CRS in detection mode for at least a week while monitoring logs. This approach helps identify legitimate traffic that might trigger false positives.

Monitor and Alert

Set up log monitoring and alerting for ModSecurity events. High anomaly scores often indicate real attacks that warrant investigation.

Keep Rules Updated

Subscribe to the OWASP CRS mailing list and update your rules regularly. Since new attack techniques are constantly discovered, rule updates provide protection against emerging threats.

Implement Defense in Depth

OWASP CRS is one layer of security. Therefore, combine it with:

  • Input validation in your application code
  • Parameterized database queries
  • Content Security Policy headers
  • Regular security audits and penetration testing

Document Your Exclusions

When creating rule exclusions, document why each exclusion was necessary. This documentation helps future administrators understand the security implications and revisit exclusions during security reviews.

For additional security hardening, consider these complementary articles:

D

Danila Vershinin

Founder & Lead Engineer

NGINX configuration and optimizationLinux system administrationWeb performance engineering

10+ years NGINX experience • Maintainer of GetPageSpeed RPM repository • Contributor to open-source NGINX modules

Leave a Reply

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

You may use these HTML tags and attributes:

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

This site uses Akismet to reduce spam. Learn how your comment data is processed.