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

Possible issue with client IP in X-Forwarded-For when checking #cloudflare? #161

Closed
geoffharcourt opened this issue Oct 4, 2024 · 2 comments · Fixed by #162
Closed

Possible issue with client IP in X-Forwarded-For when checking #cloudflare? #161

geoffharcourt opened this issue Oct 4, 2024 · 2 comments · Fixed by #162

Comments

@geoffharcourt
Copy link
Contributor

geoffharcourt commented Oct 4, 2024

We're looking at a new environment and while troubleshooting an issue, I think I found an issue in the Cloudflare request check. If this is actually a problem, it's not hole, it breaks in a way that's restrictive rather than permissive. However, I think it might fail for a not-uncommon setup.

A request from the client at 1.2.3.4 goes through Cloudflare to our ingress to our Puma server. We end up with:

REMOTE_ADDR: trusted proxy
X_FORWARDED_FOR: 1.2.3.4,a-cloudflare-ip

#cloudflare? checks if the request passed through Cloudflare by taking the remote address(es) and the IPs from X-Forwarded-For, chaining them together, and then taking elements from the left side until an untrusted IP address is encountered. However, if the client IP is on the left side of X-Forwarded-For (which seems allowable under the spec), we'll reject the request. Since appends happen to the right and we can only trust the right-most element of X-Forwarded-For, shouldn't we be evaluating from that direction and not from the left?

geoffharcourt added a commit to geoffharcourt/cloudflare-rails that referenced this issue Oct 4, 2024
In modosc#149 we got the extremely helpful `Request#cloudflare?` method which
can assist with checking if a request has been proxied through
Cloudflare. The method checks that there's an unbroken chain of trust
from the `REMOTE_ADDR` (if it exists) through forwarded IPs that only
includes trusted internal proxies and Cloudflare IP addresses.

Each proxy in the chain is supposed to append the IP of the traffic it
received to the *right side* of `X-Forwarded-For`. This means that after
evaluating `REMOTE_ADDR` we should be looking at addresses in
`X-Forwarded-For` starting on the right side, not on the left.

https://github.com/modosc/cloudflare-rails/blob/311875eef57862ecf050fb87415c56ea4823486a/lib/cloudflare_rails/check_trusted_proxies.rb#L23-L25

This ordering means that if a request has the original client as the
first address in `X-Forwarded-For` that we'll incorrectly reject it. It
also means that an attacker could craft a forged `X-Forwarded-For` that
starts with a Cloudflare IP and smuggle in a request that appears to be
valid to the method's logic.

The fix here is to continue to look at `REMOTE_ADDR` first but then to
look at `X-Forwarded-For` in right-to-left order until we run out of
trusted IP addresses (whether internal proxies or Cloudflare IPs).

Fix modosc#161
@mamhoff
Copy link

mamhoff commented Oct 7, 2024

We tried this method on Heroku, and Heroku adds their own (private IP) load balancer as REMOTE_ADDR, and the user's IP as the first thing in X-Forwarded-For. I agree #162 is the right way to do this, may I asked why you closed it?

@geoffharcourt
Copy link
Contributor Author

geoffharcourt commented Oct 7, 2024

Hi @mamhoff! That was closed by accident. Thank you for flagging.

modosc pushed a commit that referenced this issue Oct 7, 2024
In #149 we got the extremely helpful `Request#cloudflare?` method which
can assist with checking if a request has been proxied through
Cloudflare. The method checks that there's an unbroken chain of trust
from the `REMOTE_ADDR` (if it exists) through forwarded IPs that only
includes trusted internal proxies and Cloudflare IP addresses.

Each proxy in the chain is supposed to append the IP of the traffic it
received to the *right side* of `X-Forwarded-For`. This means that after
evaluating `REMOTE_ADDR` we should be looking at addresses in
`X-Forwarded-For` starting on the right side, not on the left.

https://github.com/modosc/cloudflare-rails/blob/311875eef57862ecf050fb87415c56ea4823486a/lib/cloudflare_rails/check_trusted_proxies.rb#L23-L25

This ordering means that if a request has the original client as the
first address in `X-Forwarded-For` that we'll incorrectly reject it. It
also means that an attacker could craft a forged `X-Forwarded-For` that
starts with a Cloudflare IP and smuggle in a request that appears to be
valid to the method's logic.

The fix here is to continue to look at `REMOTE_ADDR` first but then to
look at `X-Forwarded-For` in right-to-left order until we run out of
trusted IP addresses (whether internal proxies or Cloudflare IPs).

Fix #161
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 a pull request may close this issue.

2 participants