-
Notifications
You must be signed in to change notification settings - Fork 3.6k
Description
Checklist
- Have you pulled and found the error with
jc21/nginx-proxy-manager:latestdocker image?- Yes (tested with jc21/nginx-proxy-manager:2.13.6)
- Are you sure you're not using someone else's docker image?
- Yes
- Have you searched for similar issues (both open and closed)?
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:
- Create a Proxy Host and assign an Access List that only allows certain IPs (deny all others)
- Enable "Force SSL"
- From an unauthorized IP (not in the allow list), run:
curl -H "Host: private.internal.net" http://SERVER_IP - Observe: Gets
301 Moved Permanently - Follow the redirect manually:
curl -k -H "Host: private.internal.net" https://SERVER_IP - 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 thelocation /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.