Search code examples
nginxnginx-reverse-proxymod-security2

ModSecurity subrequest whitelist


I have Nginx with ModSecurity and the OWASP CRS setup being used as a reverse proxy to a couple different web servers. I am using add_after_body /gdprmessage.html; to append a GDPR acceptance to every page. Everything works fairly well, but occasionally on POST requests, the resulting page will render an ugly 403 error rather than my GDPR message. I get this message in the logs:

2020/07/24 22:42:42 [error] 19576#0: *1664601 [client 10.0.0.10] ModSecurity: Access denied with code 403 (phase 2). Matched "Operator `Ge' with parameter `5' against variable `TX:ANOMALY_SCORE' (Value: `5' ) [file "/etc/nginx/owasp-modsecurity-crs/rules/REQUEST-949-BLOCKING-EVALUATION.conf"] [line "80"] [id "949110"] [rev ""] [msg "Inbound Anomaly Score Exceeded (Total Score: 5)"] [data ""] [severity "2"] [ver "OWASP_CRS/3.2.0"] [maturity "0"] [accuracy "0"] [tag "application-multi"] [tag "language-multi"] [tag "platform-multi"] [tag "attack-generic"] [hostname "10.0.0.2"] [uri "/wp-login.php"] [unique_id "159562336247.351441"] [ref ""], client: 10.0.0.10, server: test.com, request: "POST /wp-login.php HTTP/1.1", subrequest: "/gdprmessage.html", host: "www.test.com", referrer: "https://www.test.com/wp-login.php"

The login page content renders fine, but the GDPR subrequest does not. I tried to fix this by adding the following to my ModSec rules:

SecRule REQUEST_URI "/gdprmessage.html" "id:33000,phase:1,nolog,allow,ctl:ruleRemoveById=949110"

This did not work, so I tried this to whitelist the request entirely with:

SecRule REQUEST_URI "/gdprmessage.html" "id:33000,phase:1,log,allow,ctl:ruleEngine=Off"

This also did not work. I reasoned that the REQUEST_URI possibly only had the original request, so I tested:

SecRule REQUEST_URI "/wp-login.php" "id:33000,phase:1,log,allow,ctl:ruleEngine=Off"

This "worked", but is not a solution. I looked through all of the Variables on https://github.com/SpiderLabs/ModSecurity/wiki/Reference-Manual-%28v2.x%29#Variables available to SecRule, but did not see anything that would provide the "subrequest" value.

Is there a way that I can whitelist my /gdprmessage.html subrequest, leaving the CRS in for the requests themselves?


Solution

  • I was trying to solve the issue with a ModSecurity exception. It turns out, the answer was simply adding modsecurity off to the location.

    If it helps anyone else, here is my GDPR snippet that include in my site definitions:

    add_after_body /gdprmessage.html;
    
    location = /gdprmessage.html {
        root /etc/nginx/snippets/;
        modsecurity off;
    
        if ($http_x_requested_with = XMLHttpRequest) {
            return 200;
        }
    }
    

    Testing for XMLHttpRequests ensures my GDPR message is not appended to ajax queries.