6

X-Forwarded-For header can capture the IP of the client and use this IP to implement access control.

However, the X-Forwarded-For header can be easily spoofed or manipulated.

How to prevent this or is there a better alternative to capture IP addresses?

3
  • You're using it for access control? Why?
    – schroeder
    Commented Aug 12, 2021 at 15:22
  • Need to restrict app access based on IP address (intranet only) Commented Aug 12, 2021 at 15:47
  • 1
    @ChallengeMe, the more details you provide the better. Doing so will enable the community to provide much better answers. You wouldn't seek car buying advice by just saying you want to buy a car. You'd provide as much information as possible to then have an informed decision.
    – phbits
    Commented Aug 13, 2021 at 14:46

4 Answers 4

5

The X-Forwarded-For (XFF) request header is mainly used for logging purposes as it enables the web server logs to show the original client IP address. The application could be modified to read this field and leverage it for access control though that would be a bad idea. Anyone on the internet could send a request directly to the web server (bypassing the proxy) and achieve website access by simply including an acceptable X-Forwarded-For request header. To further illustrate why this is undesirable, it would be like granting access based on the User-Agent string.

Web Server IP Access

Web servers have modules that provide access control by source IP address (nginx, IIS), however, they require the original request to hit the web server. They cannot be configured to alternatively process the XFF header. There are ways to parse the XFF value within the web server, or more specifically the website config, but that would make it a one-off configuration and isn't ideal.

Option 1: Create Intranet VIP

In the comments you mentioned only intranet access should be allowed to this website. So create a new front-end just for intranet only websites.

  1. Configure the proxy with a new intranet IP address.
  2. If using the same proxy setup, create a new backend IP address. It will become the new source IP for all proxied requests to the intranet front-end.
  3. Configure each intranet-only website to only accept requests from this new back-end IP address via IP restrictions mentioned above.

Separating this traffic from the main/public VIP will allow you to place stricter controls on it and prevent mis-configurations. IP restrictions can now be enforced via:

  • Network Firewalls
  • The System Firewall of the proxy (i.e. iptables, pf, wf.msc, etc.)
  • Within the proxy configuration

An additional bonus is that intranet IP addresses often use RFC1918 addresses making them inaccessible from the internet. Though be sure to double-check firewall rules and NAT/PAT configurations. This is be the best solution.

Option 2: Pass Through

An alternative solution is to have the proxy pass the request directly to the web server. It wouldn't be able to inject the XFF header though it wouldn't need to. Then use the web server modules to enforce access based on source IP as mentioned above.

EDIT: Depending on your configuration, a separate VIP may be needed. The issue is whether or not the proxy can read the Host: request header. If it can't, a dedicated VIP is needed so traffic can be sent directly to the back-end servers. Otherwise this will present a security issue since clients are now bypassing the proxy and making requests directly to the server. Additionally, no other websites on the back-end should accept traffic from this VIP except for the those limited to intranet-only.

Option 3: Validate Source IP Before Injecting XFF Header

The last alternative is to perform the source IP check on the proxy. While few details are provided about the setup, this functionality is available on many proxy load balancers. Download the manual and take a look at what your options are.

2
  • This is a SaaS product and there are multiple clients using the application however only one client has a requirement to make this application accessible only on their intranet. Commented Aug 13, 2021 at 6:16
  • @ChallengeMe, SaaS products have controls for this. Go with Option 3 and dig into the documentation. Additionally, the more detail you can provide the better the answers will be.
    – phbits
    Commented Aug 13, 2021 at 14:58
4

The question is fairly broad since it does not describe a specific setup, where this header is set and used. In general this header is commonly set by reverse proxies and read by the web application behind the reverse proxy.

To prevent spoofing one need to be sure that

  • The web application accepts only connections from the reverse proxy, or at least only interprets x-forwarded-for when the request comes from the reverse proxy
  • The reverse proxy always removes any existing x-forwarded-for headers and always adds its own header
  • Web application and reverse proxy agree on what is a valid request and what is a valid header, i.e. things like HTTP request smuggling or similar attacks based on interpretation differences will not work. This might be done by properly normalizing the request in the reverse proxy and rejecting any wrong or unusual requests.

How all of this can be properly implemented depends on the specific setup used.

Note also that there are alternatives that don't have the problems of using a HTTP header to transport the clients source IP. For example there is the haproxy Proxy protocol which transports the source IP information outside of the HTTP request.

Also, relying on the source IP as the only factor for access control is usually not a good idea. There are use cases though where blocking blacklisted IP, specific geographic locations, VPN and Tor exit nodes or similar is desired.

2
  • This is a SaaS product and there are multiple clients using the application however only one client has a requirement to make this application accessible only on their intranet. Commented Aug 13, 2021 at 6:16
  • 3
    @ChallengeMe: "This is a SaaS product ...." - which does not provide any real information about the particular setup. Commented Aug 13, 2021 at 6:24
3

In my blog post "Prevent Forwarded header spoofing with HTTP message signature" I describe a (brand new) way to sign the Forwarded or X-Forwarded-For header thanks to the HTTP message signature.

Basically, it uses the new HTTP headers Signature and Signature-Input from the IETF draft "HTTP Message Signatures" to sign the content of the Forwarded header with the private key of the proxy. Later the subsequent component (your backend or intranet) will check the signature validity with the proxy public key.

The forwarded request from the reverse proxy to the backend will looks like:

GET /whatever HTTP/1.1
Host: example.com
Forwarded: for=123.34.567.89
Signature-Input: proxy_signature=("forwarded")\
    ;created=1618884480;expires=1618884540;keyid="my-proxy-key-rsa-example"\
    ;alg="rsa-v1_5-sha256"
Signature: proxy_signature=:G1WLTL4/9PGSKEQbSAMypZNk+I2dpLJ6qvl2JISahlP31OO/QEUd8/\
    HdO2O7vYLi5k3JIiAK3UPK4U+kvJZyIUidsiXlzRI+Y2se3SGo0D8dLfhG95bKr\
    (...)
    M9P7WaS7fMGOt8h1kSqgkZQB9YqiIo+WhHvJa7iPy8QrYFKzx9BBEY6AwfStZAs\
    XXz3LobZseyxsYcLJLs8rY0wVA9NPsxKrHGA==:

This mechanism can be used with multiple proxies in cascade.

1
  • This feels like a solution looking for a problem. If you set up one system to proxy HTTP traffic to another system, why not use TLS between them and have confidentiality of user data in addition to signing (not to mention widespread support and decades of testing the protocol and its implementations)? Does this solve the question better than the standard practices of overwriting the user's value (for x-real-ip) or appending the real value and trusting only the last one (for x-forwarded-for)? Wouldn't the proxy just sign the user-supplied value, i.e. you still need the original solution?
    – Luc
    Commented Apr 4, 2023 at 14:15
2

A straightforward way to prevent spoofing of the X-Forwarded-For header is to

  1. unset the incoming X-Forwarded-For request header,
  2. set it again using the user's IP address in an unfalsifiable manner.

Here's an example how to do this in the Apache web server configuration:

RequestHeader unset X-Forwarded-For
RemoteIPHeader X-Forwarded-For

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .