Skip to content

Force SSL redirect is applied before Access List, leaking host existence via 301 and aiding host enumeration #5207

@Kiryuumaru

Description

@Kiryuumaru

Checklist

Describe the bug
This is a real and reproducible security issue. When both "Force SSL" and an Access List are active on a Proxy Host, HTTP requests from unauthorized IPs receive a 301 Moved Permanently to HTTPS instead of being blocked immediately.

This means an attacker can scan your IP and brute-force the Host header; if the 301 response is returned, it confirms the host (subdomain) exists, even if the attacker can't reach the service itself. Thus, sensitive/private hosts are discoverable, leaking internal subdomain names intended to be zero-trust/hidden.

Only after the browser/client follows the redirect to HTTPS does the Access List return a 403 Forbidden (if the IP is not allowed).

Nginx Proxy Manager Version
jc21/nginx-proxy-manager:2.13.6

To Reproduce
Steps to reproduce the behavior:

  1. Create a Proxy Host and assign an Access List that only allows certain IPs (deny all others)
  2. Enable "Force SSL"
  3. From an unauthorized IP (not in the allow list), run:
    curl -H "Host: private.internal.net" http://SERVER_IP
    
  4. Observe: Gets 301 Moved Permanently
  5. Follow the redirect manually:
    curl -k -H "Host: private.internal.net" https://SERVER_IP
    
  6. Observe: Gets 403 Forbidden (as expected)

Expected behavior
HTTP requests from unauthorized IPs should be blocked (403 Forbidden or 444) with no redirect. Only allowed IPs should get redirected to HTTPS. Host existence must not be leaked by a different status code.

Screenshots
Not a UI bug, but here are terminal outputs:

$ curl -H "Host: private.internal.net" http://SERVER_IP
<html>
<head><title>301 Moved Permanently</title></head>
... 

After redirect:

$ curl -k -H "Host: private.internal.net" https://SERVER_IP
<html>
<head><title>403 Forbidden</title></head>
... 

Operating System
Docker on Ubuntu/Debian, but should reproduce on all OS/container setups.

Additional context
How we verified this:

  • Inspected generated nginx config with:
    docker exec nginx-proxy-manager grep -rl "yourhost" /data/nginx/proxy_host/
    docker exec nginx-proxy-manager cat /data/nginx/proxy_host/X.conf
    
  • Noticed include conf.d/include/force-ssl.conf; is outside the location / with access rules, so HTTPS redirect happens before Access List is checked:
    server {
        ... 
        # Force SSL
        include conf.d/include/force-ssl.conf;
        ... 
        location / {
            # Access Rules
            allow 100.64.0.0/10;
            deny all;
            satisfy all;
            ...
        }
    }
  • This allows attackers to enumerate valid hosts (internal subdomains) by Host header brute force, learning subdomain names that are not meant to be public.
  • If "Force SSL" is off, the Access List blocks requests immediately as expected.
  • Related issue: Security Issue: Basic Auth allows plaintext password submission over HTTPS when internal IP is allowed via Access List (satisfy any) #4984 (basic-auth scenario, not IP ACL).

Proposed fix:
Check Access List before Force SSL redirect, so redirects only occur for allowed clients. Unauthorized IPs should receive a block and no redirect, preventing host enumeration.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions