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

Add cloudflare? method to determine if request passed through CF #149

Merged
merged 2 commits into from
Sep 17, 2024

Conversation

afn
Copy link
Contributor

@afn afn commented Aug 30, 2024

The request.cloudflare? method can be used in a Rack::Attack blocklist rule to block traffic that hasn't passed through CloudFlare.

For instance:

  Rack::Attack.blocklist('CloudFlare WAF bypass') do |req|
    !req.cloudflare?
  end

Note that the request may optionally pass through additional trusted proxies, so it will return true for any of these scenarios:

  • REMOTE_ADDR = CloudFlare
  • REMOTE_ADDR = trusted_proxy, X_HTTP_FORWARDED_FOR = CloudFlare,...
  • REMOTE_ADDR = trusted_proxy, X_HTTP_FORWARDED_FOR = trusted_proxy2,CloudFlare,...

but it will return false if CloudFlare comes after the trusted prefix of X-Forwarded-For.

The `request.cloudflare?` method can be used in a Rack::Attack blocklist rule
to block traffic that hasn't passed through CloudFlare.

For instance:

```ruby
  Rack::Attack.blocklist('CloudFlare WAF bypass') do |req|
    !req.cloudflare?
  end
```

Note that the request may optionally pass through additional trusted
proxies, so it will return true for any of these scenarios:

* `REMOTE_ADDR` = CloudFlare
* `REMOTE_ADDR` = *trusted_proxy*, `X_HTTP_FORWARDED_FOR` = CloudFlare,...
* `REMOTE_ADDR` = *trusted_proxy*, `X_HTTP_FORWARDED_FOR` = *trusted_proxy2*,CloudFlare,...

but it will return false if CloudFlare comes after the trusted prefix of
`X-Forwarded-For`.
Copy link
Owner

@modosc modosc left a comment

Choose a reason for hiding this comment

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

this is great, thanks!

please fix the linter issues and we're good. i'll update the README.md with docs around this functionality after this lands.

* Break up tests to only have one expectation per spec
* Reduce line lengths
@afn
Copy link
Contributor Author

afn commented Sep 5, 2024

Thanks for the quick reply! Rubocop warnings are fixed.

@modosc modosc merged commit f43de94 into modosc:main Sep 17, 2024
2 checks passed
geoffharcourt added a commit to geoffharcourt/cloudflare-rails that referenced this pull request 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
modosc pushed a commit that referenced this pull request 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 this pull request may close these issues.

2 participants