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

Support RFC 7239: HTTP Forwarded header #1423

Closed
wants to merge 1 commit into from

Conversation

fatkodima
Copy link
Contributor

This is an updated version of #1017 with addressed Jeremy's comments and slightly changed implementation, but the logic remains the same as described in original PR description.

@ioquatix

Closes #1017
Closes #1310

@ioquatix
Copy link
Member

This looks good, can you check failing tests?

@fatkodima
Copy link
Contributor Author

fatkodima commented Nov 30, 2019

Yes, I have checked that. Works on my machine with that ruby version and test seed. Can you trigger rebuild just in case? After that will look more closely.

Co-authored-by: Matt Bostock <matt@mattbostock.com>
@jeremyevans
Copy link
Contributor

One issue with supporting Forwarded and trying to remain backwards compatible with X-Forwarded-* is that it can introduce security issues when used with older servers that only support X-Forwarded-*. For example, an attacker can specify the Forwarded header, and if the server sets X-Forwarded-For but rack prefers Forwarded if set, that would result in using attacker specified data. If you are trying to enforce that only certain IP addresses can access a resource through a trusted proxy, this opens up a security issue.

To be safe, if both headers are present, we should only use Forwarded in preference to X-Forwarded-* if the server specifies support for it. This would require an update to SPEC, allowing an entry such as rack.use_forwarded. If the entry is present, the Forwarded header is used and X-Forwarded-* is ignored. Otherwise, X-Forwarded-* is used and Forwarded is ignored.

@tenderlove, @ioquatix Does my analysis here make sense? If so, what are your thoughts on my recommended approach?

Other than that issue, I reviewed this and I'm OK with the changes. The conflict is only due to documentation I added recently, and easily resolved. There are some unrelated changes that would better have been submitted in separate PRs, but I don't think would cause us not to merge this once the security issue is addressed.

@matthewd
Copy link
Contributor

@jeremyevans I don't see what support the server implementation would need to have -- its opinions are in REMOTE_ADDR and friends. The (IMO near-insurmountable without explicit configuration) challenge is that we need to know which header was supplied by the immediate-upstream proxy... and we have no means of direct communication there that can be trusted.*

Perhaps, as it's a relatively new header in the real world, it wouldn't be unreasonable for us to declare that an upstream that sets Forwarded is only compliant [and thus supported] if it also guarantees to strip the others -- leaving us free to either treat a mixed header set as we do now, or refuse the request a la https://github.com/rails/rails/blob/7b29bc2179702a04613068e6e6efd6cf7f343440/actionpack/lib/action_dispatch/middleware/remote_ip.rb#L136


* ... unless we can trust all reverse proxies to swallow an incoming Connection header per spec, in which case we could rely on Connection: forwarded. It's my understanding that proxies can be pretty loose with spec behaviour, though -- reverse proxies especially.

@jeremyevans
Copy link
Contributor

@matthewd I think you are correct. This probably cannot be handled by SPEC since it isn't handled by the server itself. Still, we should probably do something to prevent a security issue being introduced. If we assume that proxies that support Forwarded will correctly convert X-Forwarded-* to Forwarded as recommended by the RFC, then the appearance of both could be considered an error. However, as @ioquatix mentioned in a related issue, some proxies may set both to the same values, which also seems reasonable. Maybe the logic should be:

if forwarded_set
  if x_forwarded_set
    if forwarded_set_has_same_info_as_x_forwarded_set
       use either
    else
      raise exception
    end
  else
    use forwarded_set
  end
elsif x_forwarded_set
  use x_forwarded_set
else
  not forwarded, use other header
end

@ioquatix
Copy link
Member

ioquatix commented Feb 5, 2020

@jeremyevans can you assign this to a milestone, either 2.2 or 3.0 depending on what you think makes the most sense?

If servers need to be on board with the change, I'd say 3.0 wouldn't be a mistake.

@jeremyevans jeremyevans added this to the v3.0.0 milestone Feb 5, 2020
if forwarded = get_header(HTTP_X_FORWARDED_HOST)
if forwarded = get_http_forwarded(:host)
forwarded.last
elsif forwarded = get_header(HTTP_X_FORWARDED_HOST)
Copy link

@lunich lunich May 27, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's possible that get_header(HTTP_X_FORWARDED_HOST) == '' in some cases. For instance:

curl http://example.com -H 'X_FORWARDED_HOST;' -H 'HOST: example.com'

or

telnet example.com 80
Trying 127.0.0.1...
Connected to example.com.
Escape character is '^]'.
GET / HTTP/1.0
HOST: example.com
X_FORWARDED_HOST:


@ioquatix
Copy link
Member

@fatkodima do you mind rebasing on master?

@fatkodima
Copy link
Contributor Author

@ioquatix I'm sorry, I'm no longer in the context. Feel free to do with this PR whatever you want.

jeremyevans added a commit to jeremyevans/rack that referenced this pull request Mar 17, 2022
The Request.forwarded_priority accessor sets the priority.  Default to
considering Forwarded first, since it is now the official standard.

Also allow configuring whether X-Forwarded-Proto or X-Forwarded-Scheme
has priority, using the Request.x_forwarded_proto_priority
accessor.

Allowing configurable priorities for these headers is necessary,
because which headers should be checked depends on the environment
the application runs in.

Make Request#forwarded_authority use the last forwarded authority
instead of the first forwarded authority, since earlier forwarded
authorities can be forged by the client.

Fixes rack#1809
Fixes rack#1829
Implements rack#1423
Implements rack#1832
@ioquatix ioquatix closed this Apr 4, 2022
@ioquatix
Copy link
Member

ioquatix commented Apr 4, 2022

@jeremyevans has created a new PR for this feature/functionality.

jeremyevans added a commit that referenced this pull request Apr 5, 2022
The Request.forwarded_priority accessor sets the priority.  Default to
considering Forwarded first, since it is now the official standard.

Also allow configuring whether X-Forwarded-Proto or X-Forwarded-Scheme
has priority, using the Request.x_forwarded_proto_priority
accessor.

Allowing configurable priorities for these headers is necessary,
because which headers should be checked depends on the environment
the application runs in.

Make Request#forwarded_authority use the last forwarded authority
instead of the first forwarded authority, since earlier forwarded
authorities can be forged by the client.

Fixes #1809
Fixes #1829
Implements #1423
Implements #1832
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Better scheme detection based on RFC7239
5 participants