Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Option to not trust X-Forwarded-* headers from clients #1927

Merged
merged 3 commits into from Dec 26, 2022

Conversation

rhansen
Copy link
Collaborator

@rhansen rhansen commented Mar 16, 2022

If header values from a malicious client are passed to the backend server unchecked and unchanged, the client may be able to subvert security checks done by the backend server.

@thaJeztah
Copy link
Contributor

There's some more details about common use-cases for this on this commit; 0028cda#commitcomment-10573134 so I expect that changing this could be a breaking change.

Do you have more details about the security checks? I'd expect that a security check that depends on headers that can be controlled by the client (in the non-proxy situation a client would also be able to set these) would already be affected by this

@rhansen
Copy link
Collaborator Author

rhansen commented Mar 16, 2022

It is common practice to have a "trust proxy" option in the backend server (example). If disabled, the backend server assumes that untrusted clients might connect directly to the server. If enabled, they assume untrusted clients cannot set certain headers; those headers are reserved solely for a trusted reverse proxy. If a client can manipulate those headers then the assumption is violated which an attacker might be able to exploit.

@thaJeztah
Copy link
Contributor

Gotcha; so I guess in the example mentioned on the commit, there's a chain of proxies, and the first (AWS) proxy would be considered "trusted". Wondering if there should be an option to preserve that use-case (possibly opt-in/opt-out).

@rhansen
Copy link
Collaborator Author

rhansen commented Mar 16, 2022

There could be an option, but if so it should default to overwriting the client-supplied values for safety. The only legitimate use case for allowing client-supplied values is if there are multiple layers of trusted reverse proxies, which is rare. I think it would be better to keep the code simple rather than add a potentially dangerous option to support a rare use case. If a user does run multiple layers of reverse proxy then they can replace the template or use the nginx image directly.

@buchdag
Copy link
Member

buchdag commented Mar 24, 2022

Relevant previous PR for context : #41 and #587. I'm not really confortable about calling it a "rare use case" (based on what metric ?), breaking backward compatibility and calling it a day.

@rhansen rhansen changed the title Don't trust X-Forwarded-* headers from clients Option to not trust X-Forwarded-* headers from clients Mar 27, 2022
@rhansen
Copy link
Collaborator Author

rhansen commented Mar 27, 2022

Relevant previous PR for context : #41 and #587. I'm not really confortable about calling it a "rare use case" (based on what metric ?), breaking backward compatibility and calling it a day.

OK. I changed this PR so that the default behavior is unchanged, but added a new option that disables the forwarding of the X-Forwarded-Proto and X-Forwarded-Port headers.

@rhansen rhansen force-pushed the untrusted-headers branch 4 times, most recently from 80a6352 to 4b4fb53 Compare March 28, 2022 23:08
@rhansen rhansen changed the title Option to not trust X-Forwarded-* headers from clients feat: Option to not trust X-Forwarded-* headers from clients Mar 28, 2022
@rhansen
Copy link
Collaborator Author

rhansen commented Apr 4, 2022

Friendly ping @buchdag

@buchdag
Copy link
Member

buchdag commented Apr 10, 2022

@rhansen I took a glance at the changes, looks good to me, I'll take a closer look and merge by the end of next week at the latest.

@buchdag buchdag added type/docs PR with documentation only changes type/feat PR for a new feature labels Apr 10, 2022
@buchdag buchdag self-assigned this Apr 10, 2022
@SilverFire
Copy link
Contributor

This is a pretty serious flaw that requires a CVE registration, as it happened in other OpenSource projects: CVE-2020-28483 for go/gin, CVE-2020-35590 for WordPress, CVE-2020-13485 for CraftCMS.

Making TRUST_DOWNSTREAM_PROXY property defaulted to false leaves nginx-proxy vulnerable, unless the user takes explicit actions to enable filtering. I would prefer to use a safe default instead.

Also, the application developers usually specify trusted proxy server IP addresses explicitly and frameworks provide properties for this kind of configuration. For example, Symfony – PHP framework, Express – JS framework, Gin – Go framework.

I would probably introduce a TRUSTED_DOWNSTREAM_PROXY_NETWORKS property with the default value of private networks, as defined in network_internal.conf.

@rhansen
Copy link
Collaborator Author

rhansen commented Apr 23, 2022

I would probably introduce a TRUSTED_DOWNSTREAM_PROXY_NETWORKS property

@SilverFire Instead of or in addition to TRUST_DOWNSTREAM_PROXY? If your suggestion is in addition to this PR then we can merge this PR as-is and open a separate PR for your suggestion.

@SilverFire
Copy link
Contributor

SilverFire commented Apr 24, 2022

I'd say instead.

TRUSTED_DOWNSTREAM_PROXY_NETWORKS="0.0.0.0/0" would be an equivalent of TRUST_DOWNSTREAM_PROXY=1

TRUSTED_DOWNSTREAM_PROXY_NETWORKS="" would be an equivalent of TRUST_DOWNSTREAM_PROXY=0

@rhansen
Copy link
Collaborator Author

rhansen commented Apr 24, 2022

TRUSTED_DOWNSTREAM_PROXY_NETWORKS="" would be an equivalent of TRUST_DOWNSTREAM_PROXY=0

The trouble with this approach is the empty string (and unset) should keep the current behavior for compatibility reasons (at least until the next major release). That means that if someone wants to completely turn it off (don't trust any client headers), they would need to set it to an invalid address like 0.0.0.0/32 or 255.255.255.255/32, which is awkward. Though I guess we could accept a special value like none.

@rhansen
Copy link
Collaborator Author

rhansen commented Apr 25, 2022

@SilverFire What do you think about having TRUST_DOWNSTREAM_PROXY accept either a boolean or networks? Then we could merge this PR as-is and extend it to accept networks in a future PR.

@SilverFire
Copy link
Contributor

Damm, this pull-request is about DOWNSTREAM proxies, not UPSTERAM 😕
Yes, I think this one should be merged as-is.

I'll create a separate issue for upstream proxy trust lists.

@buchdag
Copy link
Member

buchdag commented Apr 27, 2022

@SilverFire just to check that we're on the same page:

  • this PR should be merged as is with no change to the default behaviour
  • another PR should be made regarding UPSTREAM proxy trust, with a change to the default behaviour

Did I get that right ?

@SilverFire
Copy link
Contributor

Absolutely

@rhansen
Copy link
Collaborator Author

rhansen commented May 16, 2022

Friendly ping @buchdag

If header values from a malicious client are passed to the backend
server unchecked and unchanged, the client may be able to subvert
security checks done by the backend server.
@buchdag buchdag merged commit 6f4f9ec into nginx-proxy:main Dec 26, 2022
@buchdag
Copy link
Member

buchdag commented Dec 26, 2022

Thanks @rhansen 👍

@rhansen rhansen deleted the untrusted-headers branch December 26, 2022 22:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type/docs PR with documentation only changes type/feat PR for a new feature
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants