opnsense / opnsense/plugins

[os-nginx] NAXSI blocked requests return HTTP 200 instead of 403

Open
#5,656 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
PHP
Stars
1.2k
Forks
863
Avg merge
2d 6h
Merged PRs (30d)
10

Description

Important notices
Before you add a new report, we ask you kindly to acknowledge the following:

Describe the bug
When NAXSI in the os-nginx plugin blocks a request, the request is correctly blocked internally, but the client receives HTTP 200 OK instead of an appropriate error status such as 403 Forbidden.

The generated nginx configuration uses:

DeniedUrl "/waf_denied.html";

with:

location = /waf_denied.html {
    root /usr/local/etc/nginx/views;
    access_log /var/log/nginx/waf_denied.access.log main;
}

Because this location serves the static denial page without explicitly returning an error status, the resulting response is HTTP 200.

I do not know a last known working version. The behavior is reproducible on my current installation.

To Reproduce
Steps to reproduce the behavior:

  1. Go to Services > Nginx > Configuration > HTTP(S) > Naxsi WAF Rule.

  2. Create a Basic Rule matching a test URL, for example:

    ^/xmlrpc\.php/?$
    

    with score 8.

  3. Add the rule to a Custom Security Policy configured as:

    Value: 8
    Operator: >=
    Action: BLOCK
    
  4. Enable Security Rules on the applicable nginx Location and assign the Custom Security Policy.

  5. Apply the nginx configuration.

  6. Request the blocked URL:

    curl -sS -o /dev/null -w '%{http_code}\n' https://example.com/xmlrpc.php
    
  7. The returned HTTP status is:

    200
    
  8. The nginx error log simultaneously confirms that NAXSI blocked the original request:

    NAXSI_FMT: ... uri=/xmlrpc.php&config=block&...&score0=8&zone0=URL&id0=...
    
  9. The request is also written to:

    /var/log/nginx/waf_denied.access.log
    

    where the denial response is logged with status 200.

The blocked request does not reach the upstream application, so the WAF itself is functioning correctly.

Expected behavior
A request blocked by NAXSI should return an HTTP error status, preferably:

403 Forbidden

instead of:

200 OK

The denial page may still be displayed, but it should be returned with the correct HTTP status.

Screenshots
Not required for reproduction. I can provide screenshots of the NAXSI rule, policy and Location configuration if needed.

Relevant log files
Example nginx error log entry:

NAXSI_FMT: ... server=example.com&uri=/xmlrpc.php&config=block&...&score0=8&zone0=URL&id0=...

At the same time, /var/log/nginx/waf_denied.access.log records the blocked request with HTTP status 200.

For example:

"GET /xmlrpc.php HTTP/2.0" 200 ...

The generated configuration contains:

DeniedUrl "/waf_denied.html";

location = /waf_denied.html {
    root /usr/local/etc/nginx/views;
    access_log /var/log/nginx/waf_denied.access.log main;
}

Additional context
This does not appear to be a WAF bypass. NAXSI correctly detects and blocks the request before it reaches the upstream application.

The problem is the final HTTP status returned to the client. Reporting a blocked request as 200 OK can produce misleading results in vulnerability scanners, monitoring systems, access logs and other automated security tooling.

A possible solution would be for the generated NAXSI denial handler to preserve or explicitly return a 403 status, for example by making the denial location internal and returning HTTP 403 while still optionally serving the existing denial page.

Environment
OPNsense: OPNsense 26.7.2_2-amd64
FreeBSD 15.1-RELEASE-p2
OpenSSL 3.5.7
os-nginx plugin: os-nginx (installed) | 1.36_4
Hardware: DEC850v2

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start at Services > Nginx > Configuration > HTTP(S) > Naxsi WAF Rule and inspect how the generated DeniedUrl location is produced. Reproduce with the supplied curl command and compare the nginx error and waf_denied access logs; done means blocked requests return 403 while the denial page may still be displayed and the upstream is not reached.

Written by the indexing model from the issue text.

Assessment

Tech stack
nginx
Domain
backend, security
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
55/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.