4

IETF RFC 2616 Section 4.2 allows a request to contain multiple headers with the same field-name as long as chronological order of insertion is preserved and their values can be converted into single header with a comma-separated list of values.

https://www.rfc-editor.org/rfc/rfc2616#section-4.2

message-header fields with the same field-name MAY be present in a message if and only if the entire field-value for that header field is defined as a comma-separated list [i.e., #(values)]. It MUST be possible to combine the multiple header fields into one "field-name: field-value" pair, without changing the semantics of the message, by appending each subsequent field-value to the first, each separated by a comma. The order in which header fields with the same field-name are received is therefore significant to the interpretation of the combined field value, and thus a proxy MUST NOT change the order of these field values when a message is forwarded. F5 will not overwrite any existing X-Forwarded-For. Nor will it concatenate existing X-Forwarded-For into a single comma-separated value. Instead, it will insert an additional X-Forwarded-For at the tail end of the collection.

But what about an environment with multiple clients, proxies, CDNs, traffic-managers, servers that engage in manipulation of the X-Forwarded-For collection ?

There would seem to be an advantage to enforcing a uniform practice. But what is the best practice ?

F5 BIG-IP default http profile insert header accumulates an additional X-Forwarded-For at the end of a request's pre-existing collection of XFF headers, preserving order.

AWS ELB encourages consolidation of an incoming request's multiple X-Forwarded-For into a single header containing a comma-delimited list of XFF IPs, plus the user host address, preserving order.

Other devices may employ other variations.

Does there exist an agreed-upon recommendation or de facto standard for heterogeneous environments ?

Further, is any timestamp data provided that would allow code to definitively sort X-Forwarded-For headers in chronological order of addition for the case where previous manipulations of XFF headers are suspect.

BaltoStar
  • 189
  • 2
  • 11

1 Answers1

7

Yes, there's a standard: Don't use X-Forwarded-For at all.

RFC 7239 defines the Forwarded header, which has rather different semantics from X-Forwarded-For, and new implementations ought to be using it. Unfortunately it suffers from the same problem you have identified with X-Forwarded-For here: it may be defined twice in a request or contain a comma separated list of values. Proxies are also allowed to delete it entirely.

And yes, there's a best practice: Use a different header name internally.

Remember that X-Forwarded-For and its replacement Forwarded contain untrusted input. It is trivial for a client to put whatever they want in such a header. If you really need to know the public IP address of whatever connected to your server, stick it in a different header. For instance, CloudFlare uses CF-Connecting-IP for this purpose. I've also seen Client-IP and X-Real-IP used in nginx (where you can define anything you want). Whatever name is used, your load balancers should be sending the requester's IP address in some header other than X-Forwarded-For or Forwarded.

Michael Hampton
  • 237,123
  • 42
  • 477
  • 940
  • Thanks Michael, very informative. However, our network traffic will need to continue to rely on XFF for quite some time. We have dozens of legacy client apps, proxies, traffic-managers, app-specific routers, server sites & services, many of which inject XFF or rely on presence of XFF. I'm trying to determine if should add code within gateway traffic-managers to consolidate multiple XFF into a single header with comma-separated list of values, or continue to allow accumulation of XFF collection on the request. – BaltoStar Jul 11 '15 at 19:07